正文:
今日打卡宣言
Day 11,坚持第11天!连续11天没断,感觉刷题已经逐渐变成日常。今天做了这道有序数组两数之和,分别用双指针和剪枝二分两种方式实现,收获挺大。继续冲!
LeetCode 部分
- 题目链接:leetcode.cn/problems/tw…
- 难度:中等
- 要求:返回下标(从1开始),只能使用常量额外空间
核心思路: 因为数组非递减有序,有两种高效解法:
- 最优解:左右双指针(O(n) 时间,O(1) 空间)—— 推荐!
- 剪枝二分:固定左边一个数,在右边用二分查找(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天,坚持的感觉越来越自然,危机感也在慢慢转化为行动力!
结束语
明天见! 欢迎评论区交流:
- 你更喜欢双指针还是二分写法?为什么?
- 二分查找时你通常怎么防止溢出?
- 这道题你面试中遇到过吗?