前言
题单LeetCode 热题 100的双指针部分,给出解析。
283. Move Zeroes
题目描述
Given an integer array nums
, move all 0
‘s to the end of it while maintaining the relative order of the non-zero elements.
Note that you must do this in-place without making a copy of the array.
Constraints:
1 <= nums.length <= 10^4
-2^31 <= nums[i] <= 2^31 - 1
Follow up: Could you minimize the total number of operations done?
解题方法
本题目要求将整数数组 nums
中的所有 0
移动到数组末尾,并且保持非零元素的相对顺序的问题,考虑实现一个原地算法,定义两个指针:
left
:跟踪下一个非零元素应该放置的位置。right
:遍历数组。
算法步骤:
- 初始化
left
为 0。这个指针将用于将非零元素放置在正确的位置。 - 使用
right
从 0 遍历到数组的末尾。 - 当
right
指向的元素不是0
时,将该元素放到left
位置,然后将left
加 1。 - 当所有非零元素都被放置在正确位置后,将数组中
left
到数组末尾的部分填充为0
。
代码实现
Python版本:
1 | class Solution: |
Java版本:
1 | class Solution { |
复杂度分析
时间复杂度:$O(n)$,仅需遍历一次数组。
空间复杂度:$O(1)$,无需额外存储空间。
11. Container With Most Water
题目描述
You are given an integer array height
of length n
. There are n
vertical lines drawn such that the two endpoints of the i^th
line are (i, 0)
and (i, height[i])
.
Find two lines that together with the x-axis form a container, such that the container contains the most water.
Return the maximum amount of water a container can store.
Notice that you may not slant the container.
Constraints:
n == height.length
2 <= n <= 10^5
0 <= height[i] <= 10^4
解题方法
要解决这个问题,我们可以使用双指针的做法。基本思路是从数组的两端开始,计算两个指针之间的容器面积,根据指针所指向的线的高度来判断移动哪个指针。
具体步骤如下:
- 初始化两个指针,左指针
left
指向数组的开始,右指针right
指向数组的末尾;初始化一个返回值用于存储最大面积。 - 计算当前
left
和right
指针形成的容器的面积area
:area = (right - left) * min(height[left], height[right])
。
- 更新最大面积。
- 比较
height[left]
和height[right]
:- 如果
height[left] < height[right]
,移动左指针向右(left++
),因为需要增加高度较小的一侧。 - 否则移动右指针向左。
- 如果
- 重复步骤
2~4
,直至左右指针相遇(此时底部长度为0
,无面积)。
为什么要向内移动指针?
答:向内移动指针的主要原因是寻找可能的更大容器,当前指针所指的两条线决定了容器的高度与宽度,移动指针可以帮助找到可能的更高的线,从而增加面积。并且移动指针能够有效避免重复计算已经确定面积的容器,快速找到潜在的更大面积。
代码实现
Python版本:
1 | class Solution: |
Java版本:
1 | class Solution { |
复杂度分析
时间复杂度:$O(n)$,仅需遍历一次数组。
空间复杂度:$O(1)$,无需额外存储空间。
15. 3Sum
题目描述
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]]
such that i != j
, i != k
, and j != k
, and nums[i] + nums[j] + nums[k] == 0
.
Notice that the solution set must not contain duplicate triplets.
Constraints:
3 <= nums.length <= 3000
-10^5 <= nums[i] <= 10^5
解题方法
本题要求我们在数组中找出所有和为 0
的三个数,要求三元组不能够重复。
由于给出的数组并不是单调递增的,我们可以先将数组排序。
为了方便定位,遍历数组,将遍历到的第一个数作为「锚点」,在「锚点」之后的部分用两个指针分别指向最左侧(最小值)以及最右侧(最大值),移动后两个指针来寻找剩余两个数。
这与1. Two Sum的双指针解法思路相像。
假设「锚点」下标为 i
,遍历过程:
- 如果
i > 0
且nums[i] == nums[i - 1]
,则说明nums[i]
的结果已经由相同的nums[i - 1]
加入到结果中,跳出此次循环过程。 - 剩余部分的左指针
left
和right
分别为i + 1
以及len(nums) - 1
,当left < right
时,开始如下步骤:- 记录当前三数和为
current_sum
,current_sum = nums[i] + nums[left] + nums[right]
。 current_sum > 0
,右指针值偏大,右指针移动减少和。current_sum < 0
,左指针值偏小,左指针移动增加和。current_sum == 0
,说明找到一个合适的三元组,加入到结果中。进行去重部分,移动left
和right
的直到不出现相同的数字。
- 记录当前三数和为
优化部分:
- 如果
nums[i] > 0
,此时后面的数不可能和为0
,提前结束。 - 如果
nums[i] + nums[-2] + nums[-1] < 0
,则说明在这个循环中不可能出现和为0
的三元组,因为最大的两个值和「锚点」的和都不大于0
。
代码实现
Python版本:
1 | class Solution: |
Java版本:
1 | class Solution { |
复杂度分析
时间复杂度:$O(n^2)$,其中 $n$ 为 $nums$ 的长度,排序 $O(nlogn)$。
空间复杂度:$O(1)$,无需额外存储空间。
42. Trapping Rain Water
题目描述
Given n
non-negative integers representing an elevation map where the width of each bar is 1
, compute how much water it can trap after raining.
Constraints:
n == height.length
1 <= n <= 2 * 10^4
0 <= height[i] <= 10^5
解题方法
题目大意为给你一个数组,数组内的值代表路面高度,求水坑能装多少水。
这题与上边的11题方法类似,可以使用双指针解决,但是需要注意的是,这道题目的「墙壁」构成地面高度的一部分,而11题中只需要找到两个边界,不会考虑「墙壁」对水体积的影响。
而这道题的双指针,需要采用的策略一样是移动更矮的墙壁,移动的另一侧必然会存在比它更高的墙壁。
如果更高的墙壁移动,而又找不到更高的值就没法计算了。
双指针相向移动的同时,记录遍历的墙高。如果存在更高的墙壁,更新最高高度;如果没有就把最高高度减去地面高度,就能够得出水的体积。
代码实现
Python版:
1 | class Solution: |
Java版:
1 | class Solution { |
复杂度分析
时间复杂度:$O(n)$,只需要遍历一次数组。
空间复杂度:$O(1)$,没有额外的空间。