题目描述

✅ 167. 两数之和 II - 输入有序数组

image-20260928213926870

image-20260928213926871

题意分析

在非递减排列的数组中,找到两个不同位置,使对应数值之和等于 target,返回两个位置从 1 开始的下标。数组允许重复值,只要使用的下标不同,就可以取两个相等的数。

题目保证恰好存在一组答案,并要求常数额外空间。输入已经有序,不需要重新排序;返回的是位置而不是数值,而且同一个元素不能被使用两次。

解法:有序数组双指针

核心思路

[!blue]

用 left、right 指向当前候选区间的两端。因为数组有序,左端是剩余最小值,右端是剩余最大值。比较这两个端点的和,关键不是随意让和变大或变小,而是判断哪一个端点已经不可能参与答案。

若 numbers[left] + numbers[right] < target,左端即使配上当前最大的右端也不够。其他候选都不比右端大,因此这个左端与任何剩余位置相加都偏小,可以排除它,令 left++。

若两端之和大于目标,右端即使配上当前最小的左端也已经过大。其他候选都不比左端小,因此这个右端与任何剩余位置相加都偏大,可以排除它,令 right--。

每轮只删除已经证明不可能属于答案的位置,所以答案始终保留在候选区间内。两个指针不断靠拢,最终遇到和等于目标的一对;循环使用 left < right,确保不会让同一个元素与自身配对。

解题步骤

  1. 初始化 left = 0、right = numbers.length - 1,候选区间覆盖整个数组。
  2. 只要 left < right,计算两端之和。
  3. 和等于目标时,返回 left + 1、right + 1,转换成题目规定的下标。
  4. 和偏小时排除左端,和偏大时排除右端,继续比较。
  5. 题目保证有解,合法输入会在循环内返回;末尾返回空数组仅用于补全函数返回路径。

代码实现

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int left = 0;
        int right = numbers.length - 1;

        while (left < right) {
            int sum = numbers[left] + numbers[right];

            if (sum == target) {
                return new int[] {
                    left + 1,
                    right + 1
                };
            } else if (sum < target) {
                // 数组升序,左指针右移才能增大当前和。
                left++;
            } else {
                right--;
            }
        }

        return new int[0];
    }
}
func twoSum(numbers []int, target int) []int {
    left := 0
    right := len(numbers) - 1
    for left < right {
        sum := numbers[left] + numbers[right]
        if sum == target {
            return []int{
                left + 1,
                right + 1,
            }
        } else if sum < target {
            // 当前和偏小,只能移动左指针增大和。
            left++
        } else {
            right--
        }
    }
    return []int{}
}

复杂度分析

  • 时间复杂度:$O(n)$。每轮至少有一个指针向内移动,最多移动 n - 1 次。
  • 空间复杂度:$O(1)$。除返回结果外只使用常数个变量。

关键点总结

[!green]

  • 有序性让每次比较都能排除整个端点对应的所有配对,而不只是跳过当前这一对。
  • 保留的区间始终包含答案,指针只向内移动,不需要回退或额外记账。
  • 位置不同由 left < right 保证,数值重复不会影响判断。

易错点总结

[!yellow]

  • 返回原下标会整体偏小一位,两个位置都要加 1。
  • 用 left <= right 允许两指针重合,可能把同一个元素使用两次。
  • 和偏小时移动右端,只会让当前和更小,也没有排除左端之外位置的依据。
  • 把相等数值当作重复答案而跳过,可能漏掉由两个不同位置上的相同数构成的唯一解。
  • 另建哈希表会使用线性额外空间,不满足本题的空间要求。

相似题目

题目 难度 关联与区别
1. 两数之和 简单 原题无序,需要哈希查互补值;本题有序,可以用两端指针线性收缩。
15. 三数之和 中等 先固定一个值后,剩余部分同样转化为有序两数之和,并增加结果去重。
170. 两数之和 III - 数据结构设计 简单 用哈希表查询当前元素所需的补值;本题利用有序性进一步改用双指针,该题把补值查询扩展为多次动态查询。
补充题 159. 有序数组中的目标和数对计数 中等 都用左右指针比较两端之和;补充题还要统计重复值形成的所有下标对。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2024/46257152
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!