中等LeetCode 167可逐步演示引导学习
两数之和 II
有序数组中的双指针
左右两个指针从数组两端向中间夹逼:两数之和偏大就让右指针左移,偏小就让左指针右移。
时间复杂度 O(n)空间复杂度 O(1)
所属套路:对撞双指针框架
题目描述
给定一个下标从 1 开始的升序整数数组 numbers,请从中找出两个数,使它们的和等于目标值 target,并返回这两个数的下标(下标从 1 开始)。保证有且仅有一组解,且同一个元素不能使用两次。
学习目标
能说清左右双指针为什么依赖有序、每一步排除了谁,并独立写出下标从 1 开始的实现。
已完成 0 / 6 阶段
诊断
先看条件:有序意味着什么
如果数组是乱序的,左右双指针从两端夹逼还能保证找到答案吗?先别写代码,说说你的判断和理由。
建议先完成当前阶段(提示 / 检查 / 自评),再进入下一步;也可以直接跳过。
解题思路
核心思想
左右两个指针从数组两端向中间夹逼:两数之和偏大就让右指针左移,偏小就让左指针右移。
为什么需要它
暴力枚举所有数对要 O(n²);而数组有序说明"数的大小方向是确定的"——和偏大的时候,我们确切知道该往更小的方向找,不必把所有组合都试一遍。
第一反应:直觉方案
双重循环枚举所有 (i, j) 组合,检查和是否等于 target。能过小数据,但 n 大时要检查上千万次,太慢。
关键洞察
固定左端、右端从最大值开始:如果两数之和偏大,说明"当前右端的数配上最小的左端都太大"——那这个右端配任何数都不可能合适(数组里没有比 left 更小的数了)。所以右指针可以左移,而且这次移动是安全的排除,不是碰运气:被移走的那个数永远不需要再看。每次移动确定地排除一个候选,总共最多移动 n 次,于是 O(n) 完成。
时间复杂度 O(n)空间复杂度 O(1)
易错点
- 返回的下标要从 1 开始计(题目要求),代码里是 left + 1 / right + 1
- 循环条件用 left < right 而不是 <=(同一个元素不能使用两次)
- 每次只能移动一个指针——两边同时动可能跳过正确答案
逐步演示
参考代码
1function twoSum(numbers, target) {
2 let left = 0, right = numbers.length - 1;
3 while (left < right) {
4 const total = numbers[left] + numbers[right];
5 if (total === target) {
6 return [left + 1, right + 1];
7 } else if (total < target) {
8 left += 1;
9 } else {
10 right -= 1;
11 }
12 }
13}
效果图
预测:答对 0 / 共 1 题
第 1 / 11 步