【刷题日记】以作为答案边界的方式理解双指针
本文题目难度标识:🟩简单,🟨中等,🟥困难。
热身:从「两数之和」问题铺垫
「两数之和」在 LeetCode 编号为 1,是题库的第一道题目。
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
暴力方法:最容易想到的方法,枚举数组中的每一个数 x
,寻找数组中是否存在 target - x
。
- 时间复杂度:,其中 N 是数组中的元素数量。最坏情况下数组中任意两个数都要被匹配一次。
- 空间复杂度:。
哈希表:使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 降低到 。
- 时间复杂度:,其中 N 是数组中的元素数量。对于每一个元素 x,我们可以 地寻找 target - x。
- 空间复杂度:,其中 N 是数组中的元素数量。主要为哈希表的开销。
排序 + 双指针
给你一个下标从 1 开始的整数数组 numbers
,该数组已按 非递减顺序 排列 ,请你从数组中找出满足相加之和等于目标数 target
的两个数。如果设这两个数分别是 numbers[index1]
和 numbers[index2]
,则 1 <= index1 < index2 <= numbers.length
。
以长度为 2 的整数数组 [index1, index2]
的形式返回这两个整数的下标 index1 和 index2。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。
我们可以使用上题「两数之和」中的方法解决,但是这道题的特点在于输入是一个有序的数组,我们可以利用这一点进行方法优化。官方解法有以下两种:
- 二分查找:首先固定第一个数,然后寻找第二个数,第二个数等于目标值减去第一个数的差。利用数组的有序性质,可以通过二分查找的方法寻找第二个数。为了避免重复寻找,在寻找第二个数时,只在第一个数的右侧寻找。
- 时间复杂度:,其中 n 是数组的长度。需要遍历数组一次确定第一个数,时间复杂度是 ,寻找第二个数使用二分查找,时间复杂度是 O(\log n),因此总时间复杂度是 。
- 空间复杂度:。
- 双指针
下面主要介绍双指针方法。
初始时两个指针分别指向第一个元素位置和最后一个元素的位置。每次计算两个指针指向的两个元素之和,并和目标值比较。
- 如果两个元素之和等于目标值,则发现了唯一解。
- 如果两个元素之和小于目标值,则将左侧指针右移一位。
- 如果两个元素之和大于目标值,则将右侧指针左移一位。
- 移动指针之后,重复上述操作,直到找到答案。
复杂度分析:
- 时间复杂度:,其中 n 是数组的长度。两个指针移动的总次数最多为 n 次。
- 空间复杂度:。
双指针的理解
那么上面双指针会不会把可能的解过滤掉?答案是不会。「两数之和 II」中明确表示只存在唯一的答案。
理解方式 1:抓住了答案后不会错过
假设 numbers[i]+numbers[j]==target
是唯一解,其中 0≤i<j≤numbers.length−1
。初始时两个指针分别指向下标 0
和下标 numbers.length−1
,左指针指向的下标小于或等于 i,右指针指向的下标大于或等于 j。除非初始时左指针和右指针已经位于下标 i 和 j,否则一定是左指针先到达下标 i 的位置或者右指针先到达下标 j 的位置。
- 如果左指针先到达下标 i 的位置,此时右指针还在下标 j 的右侧,
sum>target
,因此一定是右指针左移,左指针不可能移到 i 的右侧。 - 如果右指针先到达下标 j 的位置,此时左指针还在下标 i 的左侧,
sum<target
,因此一定是左指针右移,右指针不可能移到 j 的左侧。
因此使用双指针一定可以找到答案。
理解方式 2:双指针是答案的边界
双指针还可以这样理解:双指针指示的范围表示答案可能出现的范围。
- 计算两个指针
left
、right
指向的两个元素之和nums[left]+nums[right]
,如果两个元素之和等于目标值,则发现了唯一解。 - 如果两个元素之和小于目标值
nums[left]+nums[right]<target
,说明左侧指针指向的值nums[left]
不可能作为答案。因为nums[left]
和答案范围内所有其他的数相加,总会有nums[left]+nums[k]<target
,left<k<right
,因此需要将左侧指针右移一位,缩短答案范围。 - 如果两个元素之和大于目标值,则将右侧指针左移一位。原因同上。
- 当左右指针正好指向正确答案时返回结果即可。
回过头看看最初的「两数之和」问题,其实它也可以用双指针的问题解决,只不过需要额外进行 排序。
相关题目
类似题目:
可能的答案不止一个,且要求答案不得重复,这就需要考虑移动指针或遍历元素时跳过重复出现的元素:
寻找最接近答案的目标的组合,这意味着双指针需要遍历完所有的元素找到最接近的答案的组合:
本文参考
- LeetCode 相关题解