刷题日志 | LeetCode Hot 100 双指针

【个人刷题记录】因为鼠鼠还在实习,所以基本一天4题左右。两题错题+两题新题,我真的不想刷了就忘了😭

每写完一个板块就会记录一下(未来二刷三刷如果有新的体会都会更新),自我监督,让自己刷的有动力~大家一起加油🎉

2 双指针

2.1 移动零

这道题关键在于处理 p 和 q 的相对位置,如果发现 p 超过 q,应该赶紧重置 q

python 复制代码
class Solution(object):
    def moveZeroes(self, nums):
        """
        :type nums: List[int]
        :rtype: None Do not return anything, modify nums in-place instead.
        """
        # 原地移动联想到 双指针
        # 一个指针遇到0就停止,一个指针往后走遇到非0则停止
        p,q=0,1 # p遇0停,q遇非0停
        length=len(nums)
        while q < length:
            while p<length and nums[p]!=0:
                p+=1
                continue
            if q<p:
                q=p     # 初始化q的出发点这样才能保证q在p后面
            while q<length and nums[q]==0:
                q+=1
                continue
            
            if q>=length or p>=length:
                break
            elif nums[p]==0 and nums[q]!=0:
                temp=nums[p]
                nums[p]=nums[q]
                nums[q]=temp
                p+=1
                q+=1

解法2:

相对于解法1,这里优化的点在于:不需要考虑交换

我们唯一需要做的就是将快指针的值复制给慢指针即可,遇到0直接跳过

python 复制代码
class Solution(object):
    def moveZeroes(self, nums):
        """
        :type nums: List[int]
        :rtype: None Do not return anything, modify nums in-place instead.
        """
        # 双指针,新方法的核心点在于不需要进行交换操作,只需要统排非0部分即可,后面数值可以保留,最后统一用0覆盖
        not_zero=0  # 这个指针之前的都是非0
        length=len(nums)
        for i in range (length):
            if nums[i]!=0:  # 非0则放到前面
                nums[not_zero]=nums[i]
                not_zero+=1 # 指针推进
        for i in range(not_zero,length):
            nums[i]=0

2.2 盛最多水的容器

思路半对,知道是双指针,知道从两边往中间走

但是移动谁呢?

我原本考虑的是:比较两边移动后,最低位最高的那个。

这个的问题在于:需要分很多情况,需要比较 1. 左侧移动后,相对原来左侧更高怎么办 2. 左侧移动后,相对更低,同时需要计算右侧移动后是更高还是更低 3. 需要计算新低,是移动后产生的新低?还是维持原来的旧低?

但是题解的思路很清晰,我就移动当前最低的那个

理由很简单:如果想要在当前的情况下面积更大,那就只有破局当前最低的那个,只有当前最低的那个得到提升,我才能更大。如果未来这个最低的更低更低,我就算移动高的那个,也不可能比原来还大了,所以干脆就移动最低的那个才有破局希望

剪枝操作:当最大值>=最高峰*当前宽度,说明永远也不会有更大的情况了,直接break就行

python 复制代码
class Solution(object):
    def maxArea(self, height):
        """
        :type height: List[int]
        :rtype: int
        """
        # 双指针,从两边往中间走
        # 实时记录最大的面积
        # 移动后最低位尽可能高 ❌
        # 这是一个陷阱 因为考虑了未来 但是未来是有很多选择的 无法一口气全部录入
        # 所以我们只考虑现在 将现在最小的那个给移走就行 只有现在最小的有变化,后面才有可能有变化
        l=0
        r=len(height)-1
        max_area=min(height[l],height[r])*(r-l)
        maxheight=max(height)
        
        while l<r:
            if height[l]<height[r]:
                l+=1
            else:
                r-=1
            
            if l<r:
                cur_area=min(height[l],height[r])*(r-l)
                max_area=max(max_area,cur_area)
            else:
                break

            if max_area>=maxheight*(r-l):
                break

        return max_area

2.3 三数之和 🌟

这个双指针很有代表性,可以作为统一模板使用的捏:

python 复制代码
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        # 就是找 nums[i]+nums[j]=-nums[k]
        # 三重循环?会重复  --  排序后+双指针
        nums=sorted(nums)
        length=len(nums)
        ans=[]

        for i in range(length-2):
            if i > 0 and nums[i]==nums[i-1]:
                continue    # 跳出已经考虑过的值
            l=i+1
            r=length-1
            target=-nums[i]
            
            while l<r:
                s=nums[l]+nums[r]
                if s==target:
                    ans.append([nums[i],nums[l],nums[r]])
                    l+=1
                    r-=1
                    # 跳过重复元素
                    while l<r and nums[l]==nums[l-1]:
                        l+=1
                    while l<r and nums[r]==nums[r+1]:
                        r-=1
                elif s<target:  # 因为是有序的 所以这里可以进行策略性的
                    l+=1
                else:
                    r-=1
        return ans
二刷还错

错误代码:

python 复制代码
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        # 思路:不重复->先排序+跳过重复项,三元组->-a=b+c
        nums=sorted(nums)
        length=len(nums)
        ans=[]
        for i in range(length-2):
            while i>0 and nums[i]==nums[i-1]:
                continue
            
            target=-nums[i]
            l=i+1
            r=length-1
            while l<r:
                while l<r and nums[l]==nums[l+1]:
                    l+=1
                while l<r and nums[r]==nums[r-1]:
                    r-=1

                s=nums[l]+nums[r]

                if s<target:    # 要增加,只有l移动才行
                    l+=1
                elif s>target:
                    r-=1
                else:
                    ans.append([nums[i],nums[l],nums[r]])
                    l+=1
                    r-=1
                    
        return ans

错误原因:

  1. 超时while i>0 and nums[i]==nums[i-1]: continue导致无限循环,应该将 while 改为 if
  2. 逻辑问题匹配成功之后 才需要消重,匹配前直接提前跳过会漏掉大量配对;比如 0,0,0,0,0,要是没匹配前消重,直接别玩了

2.4 接雨水

暴力按层计算,会超时

python 复制代码
class Solution(object):
    def trap(self, height):
        """
        :type height: List[int]
        :rtype: int
        """
        # 从下往上走,每次整个数组-1,看两个非0数字之间0的数量,就是该层积水数量
        hightest=0
        sec_hightest=0
        for h in height:
            if h>hightest:
                sec_hightest=hightest
                hightest=h

        length=len(height)
        total=0

        for water_h in range(sec_hightest):
            l=0
            r=0
            cur_cnt=0
            # 找到第一个高于水平面的柱子
            while height[l]<=water_h:
                    l+=1
            # print("first h",l)
            while r<length:
                # 找到第一个水平面
                while l<length and height[l]>water_h:
                    l+=1
                # 走水
                r=l+1
                while r<length and height[r]<=water_h:
                    r+=1
                
                if r<length:
                    cur_cnt+=r-l
                    l=r
                else:
                    break
            
            # print("cur_cnt",cur_cnt)
            total+=cur_cnt
        return total

正解1:按列计算 空间换时间

每一列的积水量=min(左侧最高柱子,右侧最高柱子)-当前列柱子高度

所以对于每一列,我们都应该知道他的左右最高柱子是什么,然后比较二者最小的。可以通过暴力 ,也可以提前扫一遍存下来

python 复制代码
class Solution(object):
    def trap(self, height):
        """
        :type height: List[int]
        :rtype: int
        """
        # 每一列的积水量=min(左侧最高柱子,右侧最高柱子)-当前列柱子高度
        length=len(height)
        leftmax=[0]*length
        rightmax=[0]*length
        colwater=[0]*length
        
        leftmax[0]=height[0]
        for i in range(1,length):
            leftmax[i]=max(leftmax[i-1],height[i]) # 注意,需要将自己也列入其中,为了让后面的参考
        
        rightmax[-1]=height[-1]
        for i in range(length-2,-1,-1):
            rightmax[i]=max(rightmax[i+1],height[i])

        for i in range(length):
            colwater[i]=min(leftmax[i],rightmax[i])-height[i]
        
        return sum(colwater)

正解2:双指针

根据正解一可以发现,其实我们的 leftmax rightmax 数组只使用过一次,所以是可以优化掉的

还记得双指针最重要的是什么吗?如果想要真正发挥双指针的作用,而不是遍历,则是需要一种贪心 策略在里面的。比如这道题,其实贪心来,应该是每次移动对应侧矮的那个指针。因为如果移动对应侧高的那个,新的指针就得考虑新的双侧高度的情况了,反之,如果移动矮的那侧,则只需要直接根据矮的高度进行计算

python 复制代码
class Solution(object):
    def trap(self, height):
        """
        :type height: List[int]
        :rtype: int
        """
        # 双指针:策略性移动最矮的那个
        # 当前计算的是 左右指针所在位置的那个
        # 移动的是对应侧矮的指针 比如 leftmax矮 那就移动 left 因为如果leftmax矮 说明积水量只会根据leftmax的变化而变化
        n=len(height)
        leftmax=height[0]
        rightmax=height[n-1]
        # 指向两边的指针
        l=0
        r=n-1
        total=0
        
        while l<r:
            if leftmax<rightmax:
                total+=leftmax-height[l]
                l+=1
            else:
                total+=rightmax-height[r]
                r-=1

            leftmax=max(leftmax,height[l])
            rightmax=max(rightmax,height[r])
        return total
相关推荐
退休倒计时1 小时前
【每日一题】LeetCode 17. 电话号码的字母组合 TypeScript
算法·leetcode·typescript
粘稠的浆糊1 小时前
[AtCoder - abc465_d ]X to Y题解
数据结构·c++·算法
鱼儿也有烦恼1 小时前
数据结构合集
算法·algorithm
兰令水2 小时前
hot100【acm版】【2026.7.11/12打卡-java版本】
java·开发语言·数据结构·算法·职场和发展
研來如此2 小时前
图像文件大小
人工智能·算法·计算机视觉
千纸鹤安安2 小时前
开源 Agent 框架实战:用自然语言替代 Crontab 做运维
算法
从零开始的代码生活_2 小时前
C++ string 详解:常用接口、字符串算法与深拷贝实现
开发语言·c++·后端·学习·算法
Keven_112 小时前
算法札记:差分约束系统为啥叫差分约束系统Σ(っ °Д °;)っ
算法·差分约束系统
XWalnut2 小时前
LeetCode刷题 day29
java·算法·leetcode