首页 > 其他分享 >面试高频:双指针---6题14图一次搞懂

面试高频:双指针---6题14图一次搞懂

时间:2023-12-05 21:33:45浏览次数:40  
标签:盛水值 14 min 复杂度 height --- 搞懂 挡板 指针

使用双指针是降低算法复杂度的一个有效途径,有些问题的暴力解法时间复杂度是O(n^2),但使用双指针可以大幅度降低算法复杂度。如果面试者能将求解过程从暴力法优化到双指针,说明面试者的基础知识、代码能力、逻辑思维都是十分扎实的。

同贪心算法一样,双指针的难点在于自己想不出、别人的理解不了、正确性难以证明。

常用的双指针法有一下几类:

  1. 左右指针:两个指针,相向而走,中间相遇。
  2. 快慢指针:两个指针,有快有慢,同向而行。
  3. 灵活运用:两个指针,灵活运用,伺机而动。

下面将结合具体题目,从暴力做法一步一步优化到双指针,攻克想不出、看不懂、不会用的难题。

左右指针

左右指针地熟练使用需要一定经验的积累,如果接触的较少,是不容易想出来的。下面将以题目为例,一步步从暴力解法优化到双指针。

思路:先找出暴力解法,根据题目性质,优化到双指针

例题一:盛最多水的容器

给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点(i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为(i, ai)(i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

 

 

示例 :

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

这道题的最优解法是左右双指针法。双指针法的难点在于难于想到,难以证明。接下来将一步一步地从暴力解法优化到双指针法。证明也就很简单了。

暴力法:

找出每一种情况,求出盛水值,最大的就是答案。

1. i指向左挡板,从第一块到遍历倒数第二块。

2. j指向右挡板,从倒数第一块遍历到i后面那一块。

3. res保存最大盛水值。

4. 返回res

代码:

//cpp
class Solution {
public:
    int maxArea(vector<int>& height) {
        if(height.size() <= 1) return 0;
        int res = 0;//保存结果
        for(int i = 0; i < height.size() - 1; i++)//以i为左挡板,从O开始
        {
            for(int j = height.size() - 1; j > i; j--)//以j为右挡板,从height.size() - 1开始
            {
                int L = j - i;//底边长度
                int H = min(height[i], height[j]);//对短的挡板为高
                res = max(res, L * H);//取最大值
            }
        }
        return res;
    }
};

 

S[l,r]表示以第l块板为左挡板,第r块板为右挡板的盛水值。S[l, r]就等于min(height[l], height[r]) / (r - l)

以输入[1,8,6,2,5,4,8,3,7]为例,共8块挡板,看看都计算了哪些值:

 

 

 

 

优化:

1. 开始时,l指向第0块挡板,r指最后一块挡板。S[l, r]=min(1, 7) * (8 - 0) = 8

 

 

 

 

 

2. 向内移动指向较长挡板的r指针,盛水面积不会变大。向内移动r指针的时候,盛水值S[l, r] = min(height[l], height[r]) / (r - l)min(height[l], height[r]) 不会大于height[l],也就是不会大于7。(r - l)会随着r内移减小。所以向内移动r指针的时候,盛水值不可能变大。也就是S[0,8]肯定大于S[0,7],S[0,6],S[0,5],S[0,4],S[0,3],S[0,2],S[0,1]

因此知道了以height[0]为左挡板的最大盛水值。以后计算就不用考虑height[0]了

 

 

 

 

 

3. 子问题就变成了:在[8,6,2,5,4,8,3,7]中求出最大盛水值,然后与刚才的求出的以height[0]为左挡板的最大盛水值比较大小,大的为答案。

 

 

 

 

 

4. 子问题求解时,用l指向指向第0块挡板,也就是height[1],r指最后一块挡板,也就是height[8]S[l, r]=min(8, 7) / (8 - 1) = 56

这个时候,如果向内移动指向较长挡板的l指针,盛水面积不会变大。

因为向内移动l指针的时候,盛水值S[l, r] = min(height[l], height[r]) / (r - l)min(height[l]height[r]) 不会大于height[r],也就是不会大于7。(r - l)会随着l内移减小。所以向内移动l指针的时候,盛水值不可能变大。也就是S[1,8]肯定大于S[2,8],S[3,8],S[4,8],S[5,8],S[6,8],S[7,8],就知道了以height[8]为右挡板的最大盛水值。

 

 

 

 

 

 

5. 子问题可以再次缩小,就变成了:在[8,6,2,5,4,8,3]中求出最大盛水值,然后与刚才的求出的以height[0]为左挡板的最大盛水值,以height[8]为右挡板的最大盛水值比较大小,大的为答案。

6. 以此类推,每次就能求出以最外侧两个挡板中,短的挡板为边界的最大值。然后再一次缩小问题。就不需要计算所有的情况了,只需要计算出每块挡板为边界的最大值,然后求出其中的最大值,就是答案。

7. 这样下去,求解空间就变为了:

 

 

 

 

 

代码:

//cpp
class Solution {
public:
    int maxArea(vector<int>& height) {
        if(height.size() <= 1) return 0;
        int res = 0;//保存答案
        int l = 0, r = height.size() - 1;//开始时,l指向最左边的挡板,r指向最右边的挡板
        while(l < r)//如果l,r之间还有挡板
        {
            res = max(min(height[l], height[r]) * (r - l), res);//计算盛水值
            if(height[l] <= height [r])//谁小谁以后就不用再考虑 
                l++;
            else
                r--;
        }
        return res;
    }
};

时间上,l,r指针遍历一遍,所以时间复杂度是O(n)。空间上,没有开辟与输入有关的空间,所以空间复杂度是O(1)。

 

https://zhuanlan.zhihu.com/p/335817266

标签:盛水值,14,min,复杂度,height,---,搞懂,挡板,指针
From: https://www.cnblogs.com/gongxianjin/p/17878329.html

相关文章

  • 无涯教程-Erlang - write函数
    此方法用于将内容写入文件。write-语法write(FileHandler,text)FileHandler-这是文件的句柄。该句柄是使用file:open操作时将返回的句柄。text        - 需要添加到文件中的文本。write-示例-module(helloLearnfk).-export([start/0]).sta......
  • Linux p14 组管理和权限管理
    组管理和权限管理一、组管理Linux组的基本介绍Linux组:在Linux中的每个用户必须属于一个组,不能独立于组外。在Linux中每个文件有所有者、所在组、其它组的概念。文件/目录所有者(User):一般为文件的创建者,谁创建了该文件,就自然的成为该文件的所有者。文件/目录所在组(Gro......
  • sql-3.1外键
     从表CREATETABLE`student1`(`id`INTNOTNULLAUTO_INCREMENTCOMMENT'id',`greadid`INT(11)NOTNULLCOMMENT'gradeid',`pwd`VARCHAR(23)COMMENT'密码',`name`VARCHAR(32)NOTNULLCOMMENT'名字',`add`VARCHAR......
  • .NET Core 开发的支付SDK集 - paylink
    一套基于.NETCore开发的支付SDK集-paylink 前言在我们的日常工作开发中对接一些第三方支付是比较常见的,如最常见的就是支付宝、微信支付的对接。今天给大家推荐一个基于.NETCore开发的支付SDK集:paylink,它极大简化了API调用及通知的处理流程从而大大提供我们的工作生......
  • 每日总结-23.12.4
    packagecom.example.demo2.controller;importcom.example.demo2.common.AjaxResult;importcom.example.demo2.entity.gongWenInfo;importcom.example.demo2.mapper.gongWenMapper;importorg.springframework.beans.factory.annotation.Autowired;importorg.spring......
  • 每日总结-23.12.5
    packageInterface;importjavax.swing.*;importjavax.swing.table.DefaultTableCellRenderer;importjavax.swing.table.DefaultTableModel;importjava.awt.*;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;importjava.io.BufferedRea......
  • Go - two bcrypt hashes of the same password are NOT equal
     packagemainimport("fmt""golang.org/x/crypto/bcrypt")funcmain(){password:="abcdef"hashedPassword1,_:=bcrypt.GenerateFromPassword([]byte(password),bcrypt.DefaultCost)fmt.Println(strin......
  • sping_boot学习系列-搭建springboot项目工程
    搭建springboot工程方式一.通过idea SpringInitializr搭建详细步骤:1.创建一个新项目File->New->Project...2.项目环境配置选择SpringInitializr(20231205:注最低版本是jdk17,若搭建jdk8版本的,可先搭建jdk17版本的,修改pom.xml文件)默认即可,可修改项目名称选择maven......
  • 7-3 考试座位号
    每个PAT考生在参加考试时都会被分配两个座位号,一个是试机座位,一个是考试座位。正常情况下,考生在入场时先得到试机座位号码,入座进入试机状态后,系统会显示该考生的考试座位号码,考试时考生需要换到考试座位就座。但有些考生迟到了,试机已经结束,他们只能拿着领到的试机座位号码求助于......
  • Linux监测工具 - NetData
    安装yuminstall-ynetdata.x86_64配置vi/etc/netdata/netdata.conf##修改默认端口,默认为:19999defaultport=19999##修改bindto=localhost为bindto=0.0.0.0bindto=0.0.0.0##重启systemctlrestartnetdata访问地址http://localhost:19999/netda......