前言
题单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.length2 <= n <= 10^50 <= 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.length1 <= n <= 2 * 10^40 <= height[i] <= 10^5
解题方法
题目大意为给你一个数组,数组内的值代表路面高度,求水坑能装多少水。
这题与上边的11题方法类似,可以使用双指针解决,但是需要注意的是,这道题目的「墙壁」构成地面高度的一部分,而11题中只需要找到两个边界,不会考虑「墙壁」对水体积的影响。
而这道题的双指针,需要采用的策略一样是移动更矮的墙壁,移动的另一侧必然会存在比它更高的墙壁。
如果更高的墙壁移动,而又找不到更高的值就没法计算了。
双指针相向移动的同时,记录遍历的墙高。如果存在更高的墙壁,更新最高高度;如果没有就把最高高度减去地面高度,就能够得出水的体积。
代码实现
Python版:
1 | class Solution: |
Java版:
1 | class Solution { |
复杂度分析
时间复杂度:$O(n)$,只需要遍历一次数组。
空间复杂度:$O(1)$,没有额外的空间。