每天五分钟:双指针-LeetCode高频题_解析

283. 移动零https://leetcode.cn/problems/move-zeroes/

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

python 复制代码
输入: nums = [0,1,0,3,12]

输出: [1,3,12,0,0

⭐ 本题使用的是"双指针 · 同向指针(slow/fast)"

  • right 指针(fast):遍历数组,寻找所有非零数字

  • left 指针(slow):指向下一个应该放非零数字的位置

复制代码
初始数组:
[0, 1, 0, 3, 12]
 L
 R

1️⃣ R=0,nums[0] = 0 → 不动
[0, 1, 0, 3, 12]
 L
 R
2️⃣ R=1,nums[1] = 1 → 和 L(=0) 交换

[1, 0, 0, 3, 12]
    L
    R
3️⃣ R=2,nums[2] = 0 → 不动

[1, 0, 0, 3, 12]
    L
       R
4️⃣ R=3,nums[3] = 3 → 与 L(=1) 交换

[1, 3, 0, 0, 12]
       L
          R
5️⃣ R=4,nums[4] = 12 → 与 L(=2) 交换

[1, 3, 12, 0, 0]
          L
             R

最终结果:

[1, 3, 12, 0, 0]

python代码如下:

python 复制代码
class Solution:
    def moveZeroes(self, nums):
        left, right = 0, 0
        while right < len(nums):
            if nums[right] != 0:
                nums[left], nums[right] = nums[right], nums[left]
                left += 1
            right += 1

11. 盛最多水的容器https://leetcode.cn/problems/container-with-most-water/

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和(i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

⭐ 本题使用的是"双指针 · 对撞指针"

1. 定义两个指针

left 指向最左边的木板(0) right 指向最右边的木板(n-1)

2. 当前这两块木板能装多少水?

python 复制代码
width = right - left 
height_container = min(height[left], height[right]) 
area = width * height_container

用一个变量 max_area 记录最大值。

3. 关键问题:下一步应该动谁?

只有移动矮板,才可能出现更高的短板,增加面积

python 复制代码
left = 0, right = n - 1, max_area = 0

当 left < right 时循环:

计算当前面积:
area = (right - left) * min(height[left], height[right])

更新 max_area

如果 height[left] < height[right]:
left += 1

否则:
right -= 1

循环结束,返回 max_area

python代码如下:

python 复制代码
class Solution:
    def maxArea(self, height):
        left, right = 0, len(height) - 1
        max_area = 0

        while left < right:
            # 当前容器宽度
            width = right - left
            # 短板高度
            h = min(height[left], height[right])
            # 当前面积
            area = width * h
            # 更新最大值
            max_area = max(max_area, area)

            # 谁矮谁挪窝
            if height[left] < height[right]:
                left += 1
            else:
                right -= 1

        return max_area

15. 三数之和https://leetcode.cn/problems/3sum/

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

python 复制代码
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

⭐ 本题使用的是 排序+"双指针 · 对撞指针"

想象一个场景:

有一条队伍,队伍里站着很多同学,每个同学手里拿着一个"分数",可能是负的,也可能是正的。

老师的任务是:

"在这堆同学里,找出所有 三人小组

让他们手里的分数加起来 刚好等于 0

而且 同样的三人组合不能算两次。"

  1. 先让同学按分数从小到大排好队(排序)

  2. 接着,老师做下面的事:

    • 从左到右,一个一个选"第一个同学"当队长(下标 i)

    • 然后在他右边,用两只手:

      • 左手指向队长右边第一个人(left)

      • 右手指向队伍最后一个人(right)

    • 这三个同学(队长、left、right)的分数之和:

      • 如果 太小了(<0) :说明分数不够,需要更大一点

        👉 左手往右挪一步(left++)

      • 如果 太大了(>0) :说明分数太大,需要小一点

        👉 右手往左挪一步(right--)

      • 如果 刚好等于 0

        👉 记录下这组三人组

        👉 然后左右手都挪一格,继续找下一组

并且,老师还有一个要求:"如果遇到 一模一样的分数的同学,同样的选法就不再重复试一遍(去重)。"

就这样,把原本需要三重循环才能做的事情,用两重循环就搞定了。

python代码如下:

python 复制代码
class Solution:
    def threeSum(self, nums):
        nums.sort()          # 1. 先排序
        n = len(nums)
        res = []

        for i in range(n):
            # 剪枝1:如果第一个数已经 > 0,后面更不可能凑成 0
            if nums[i] > 0:
                break

            # 去重1:跳过相同的起点
            if i > 0 and nums[i] == nums[i - 1]:
                continue

            left, right = i + 1, n - 1

            while left < right:
                s = nums[i] + nums[left] + nums[right]

                if s < 0:
                    # 总和太小,左指针右移,让和变大
                    left += 1
                elif s > 0:
                    # 总和太大,右指针左移,让和变小
                    right -= 1
                else:
                    # 找到一个三元组
                    res.append([nums[i], nums[left], nums[right]])

                    # 去重2:跳过相同的 left
                    while left < right and nums[left] == nums[left + 1]:
                        left += 1
                    # 去重3:跳过相同的 right
                    while left < right and nums[right] == nums[right - 1]:
                        right -= 1

                    # 移动到下一对
                    left += 1
                    right -= 1

        return res
相关推荐
断剑zou天涯20 小时前
【算法笔记】Manacher算法
java·笔记·算法
monster000w21 小时前
大模型微调过程
人工智能·深度学习·算法·计算机视觉·信息与通信
小小晓.21 小时前
Pinely Round 4 (Div. 1 + Div. 2)
c++·算法
SHOJYS21 小时前
学习离线处理 [CSP-J 2022 山东] 部署
数据结构·c++·学习·算法
biter down21 小时前
c++:两种建堆方式的时间复杂度深度解析
算法
zhishidi21 小时前
推荐算法优缺点及通俗解读
算法·机器学习·推荐算法
WineMonk21 小时前
WPF 力导引算法实现图布局
算法·wpf
2401_8370885021 小时前
双端队列(Deque)
算法
ada7_1 天前
LeetCode(python)108.将有序数组转换为二叉搜索树
数据结构·python·算法·leetcode
奥特曼_ it1 天前
【机器学习】python旅游数据分析可视化协同过滤算法推荐系统(完整系统源码+数据库+开发笔记+详细部署教程)✅
python·算法·机器学习·数据分析·django·毕业设计·旅游