【Day 11】公开打卡第11天 | LeetCode 167. 两数之和 II - 输入有序数组(双指针 + 剪枝二分)

0 阅读3分钟

正文

今日打卡宣言

Day 11,坚持第11天!连续11天没断,感觉刷题已经逐渐变成日常。今天做了这道有序数组两数之和,分别用双指针和剪枝二分两种方式实现,收获挺大。继续冲!

LeetCode 部分

  • 题目链接:leetcode.cn/problems/tw…
  • 难度:中等
  • 要求:返回下标(从1开始),只能使用常量额外空间

核心思路: 因为数组非递减有序,有两种高效解法:

  1. 最优解:左右双指针(O(n) 时间,O(1) 空间)—— 推荐!
  2. 剪枝二分:固定左边一个数,在右边用二分查找(O(n log n)),通过限制二分范围(从 i+1 开始)进行剪枝。

代码 1 - 最优双指针写法(推荐)

C++

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int left = 0;
        int right = numbers.size() - 1;
        
        while (left < right) {
            int sum = numbers[left] + numbers[right];
            if (sum == target) {
                return {left + 1, right + 1};
            } else if (sum < target) {
                left++;   // 和太小,左指针右移
            } else {
                right--;  // 和太大,右指针左移
            }
        }
        return {};
    }
};

代码 2 - 剪枝二分写法(你提供的思路优化版)

C++

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int n = numbers.size();
        
        for (int i = 0; i < n - 1; ++i) {
            int findNum = target - numbers[i];
            int l = i + 1;      // 关键剪枝:只在 i 后面查找
            int r = n - 1;
            
            while (l <= r) {
                int mid = l + (r - l) / 2;   // 防溢出写法
                if (numbers[mid] == findNum) {
                    return {i + 1, mid + 1};
                } else if (numbers[mid] > findNum) {
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            }
        }
        return {};
    }
};

易错点 & 面试追问

  • 双指针必须用 left < right,不能 <=(避免同一个元素用两次)。
  • 二分查找时一定要从 i+1 开始(你提到的剪枝点),否则会重复使用元素或超时。
  • 二分推荐使用 l + (r - l)/2 防止溢出。
  • 因为数组非递减有序,遇到重复元素也不影响(题目保证唯一答案)。
  • 面试常问:如果数组无序怎么办?(回到哈希表 O(n) 解法)

知识点部分

有序数组两数之和的两种思路对比

  • 双指针:时间 O(n),空间 O(1),代码最简洁,是本题最优解。

  • 剪枝二分:时间 O(n log n),通过限制查找范围实现剪枝,适合练习二分边界。

  • 经典二分模板回顾:

    C++

    while (l <= r) {
        int mid = l + (r - l) / 2;
        if (nums[mid] == target) return mid;
        else if (nums[mid] > target) r = mid - 1;
        else l = mid + 1;
    }
    

今日感悟

今天把有序两数之和的两种方法都手写了一遍,双指针写完后感觉特别清晰优雅,而剪枝二分让我更深刻理解了“利用有序性进行剪枝”的思想。以前看到有序数组题总想着哈希表,今天才意识到双指针才是最优选择。公开打卡第11天,坚持的感觉越来越自然,危机感也在慢慢转化为行动力!

结束语

明天见! 欢迎评论区交流:

  • 你更喜欢双指针还是二分写法?为什么?
  • 二分查找时你通常怎么防止溢出?
  • 这道题你面试中遇到过吗?