LeetCode 167中等对撞双指针
两数之和 II 图解题解
数组已经排好序了,这个条件不用白不用——根本不需要哈希表。
升序数列就像价格标签从低到高排成一排,你从两端各拿一件:两件总价太贵,就把右边那件换成更便宜的;总价太便宜,就把左边那件换成更贵的。顺序已知,每次都确定该挪哪头,不用像无序时那样把每个价格都记下来慢慢查。
这道题到底在问什么
numbers 是非递减有序数组,找下标 i<j 使 numbers[i]+numbers[j]=target,返回 [i+1, j+1](1-based)。题目保证恰有一组解。
- 输入
- numbers=[2,7,11,15], target=9
- 输出
- [1, 2] (2 + 7 = 9)
最优解:一步一步想明白
- 3记住这条「大了缩右、小了扩左」,下面每一帧都在套它。
- 4左右两端 1+25=26,比 14 大了。要让和变小,只能动右边——把右指针往左挪,换一个更小的数。
- 5右指针左移到下标 10(值 22)。左指针不动,因为左边已经是当前最小、再小没意义。
- 6左右两端 1+22=23,比 14 大了。要让和变小,只能动右边——把右指针往左挪,换一个更小的数。
- 7右指针左移到下标 9(值 19)。左指针不动,因为左边已经是当前最小、再小没意义。
- 8左右两端 1+19=20,比 14 大了。要让和变小,只能动右边——把右指针往左挪,换一个更小的数。
- 9右指针左移到下标 8(值 17)。左指针不动,因为左边已经是当前最小、再小没意义。
- 10左右两端 1+17=18,比 14 大了。要让和变小,只能动右边——把右指针往左挪,换一个更小的数。
- 11右指针左移到下标 7(值 15)。左指针不动,因为左边已经是当前最小、再小没意义。
- 12左右两端 1+15=16,比 14 大了。要让和变小,只能动右边——把右指针往左挪,换一个更小的数。
- 13右指针左移到下标 6(值 12)。左指针不动,因为左边已经是当前最小、再小没意义。
- 14左右两端 1+12=13,比 14 小了。要让和变大,只能动左边——把左指针往右挪,换一个更大的数。
- 15左指针右移到下标 1(值 3)。右指针不动,因为右边已经是当前最大、再大就更超了。
- 16左右两端 3+12=15,比 14 大了。要让和变小,只能动右边——把右指针往左挪,换一个更小的数。
- 17右指针左移到下标 5(值 10)。左指针不动,因为左边已经是当前最小、再小没意义。
- 18左右两端 3+10=13,比 14 小了。要让和变大,只能动左边——把左指针往右挪,换一个更大的数。
- 19左指针右移到下标 2(值 5)。右指针不动,因为右边已经是当前最大、再大就更超了。
- 20左右两端 5+10=15,比 14 大了。要让和变小,只能动右边——把右指针往左挪,换一个更小的数。
- 21右指针左移到下标 4(值 8)。左指针不动,因为左边已经是当前最小、再小没意义。
- 22左右两端 5+8=13,比 14 小了。要让和变大,只能动左边——把左指针往右挪,换一个更大的数。
- 23左指针右移到下标 3(值 6)。右指针不动,因为右边已经是当前最大、再大就更超了。
- 24左右两端 nums[3]=6 与 nums[4]=8,加起来正好 14 = 14,命中!这两个数就是答案。
- 25命中的两个数绿色高亮。从两端往中间走,l 一路向右、r 一路向左,各只走一遍,O(n) 就找到了答案。
⚠️ 容易写错的地方
✗ 错:返回下标从 0 开始
✓ 对:题目要 1-based,要 return [l+1, r+1]
本题特别规定下标从 1 计数,最易丢分
✗ 错:和大了去动左指针
✓ 对:和大了应该缩右 r--
左指针右移只会让和更大,方向反了
✗ 错:用 l<=r 当循环条件
✓ 对:应是 l<r
同一个元素不能用两次,l 和 r 不能重合
完整代码(Python / C++ / Java)
Python
def twoSum(numbers, target):
l, r = 0, len(numbers) - 1
while l < r:
s = numbers[l] + numbers[r]
if s == target:
return [l + 1, r + 1] # 1-based
elif s > target:
r -= 1 # 和太大,缩右
else:
l += 1 # 和太小,扩左C++
vector<int> twoSum(vector<int>& numbers, int target){
int l = 0, r = numbers.size() - 1;
while(l < r){
int s = numbers[l] + numbers[r];
if(s == target) return {l + 1, r + 1};
else if(s > target) r--; // 缩右
else l++; // 扩左
}
return {};
}Java
public int[] twoSum(int[] numbers, int target) {
int l = 0, r = numbers.length - 1;
while (l < r) {
int s = numbers[l] + numbers[r];
if (s == target) {
return new int[]{l + 1, r + 1}; // 下标从 1 开始
} else if (s > target) {
r--; // 和太大,右指针左移
} else {
l++; // 和太小,左指针右移
}
}
return new int[0];
}复杂度
时间
O(n)
l、r 从两端往中间走,合计最多走 n 步
空间
O(1)
只用两个指针,不开额外数组/哈希
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 两数之和 II 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
如果数组无序,这个对撞双指针还能用吗?+
不能直接用。对撞双指针依赖「有序」的单调性。无序数组用哈希表一遍扫(LC1 两数之和),O(n) 时间 O(n) 空间。
为什么本题不用哈希表?+
可以用,但既然数组已有序,对撞双指针能做到 O(1) 额外空间,比哈希更省,是利用前提的更优解。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 两数之和 II 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
把这道题真正拿下
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。