
题目 1:两数之和 II - 输入有序数组
链接:167. 两数之和 II - 输入有序数组 - 力扣(LeetCode)
题目分析
给你一个下标从 1 开始的整数数组 numbers,该数组已按非递减顺序排列 。请你从数组中找出满足相加之和等于目标数 target 的两个数。返回这两个数对应的下标(下标从 1 开始),且每个输入只有一个答案,不能重复使用同一个元素。 例:numbers = [2,7,11,15], target = 9,答案 [1,2]。 核心:数组有序 + 唯一解,适合用相向双指针,左右两端夹逼,O (n) 解决,比哈希表更省空间、更能体现有序价值。
算法原理
利用有序数组「两端夹逼」的单调性:
- 左指针
left = 0,右指针right = n-1 - 计算
sum = numbers[left] + numbers[right]sum == target:找到答案,返回[left+1, right+1]sum < target:和太小,需要更大的数,左指针右移 →left++sum > target:和太大,需要更小的数,右指针左移 →right--
- 直到
left >= right结束
为什么可行?数组有序,左指针右移一定让和变大,右指针左移一定让和变小,两个指针只能单向移动,保证不会漏解。
代码编写
cpp
vector<int> twoSum(vector<int>& numbers, int target) {
int left = 0, 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 {-1, -1};
}
总结
相向双指针最基础的模板:有序数组两端夹逼,和太小动左、和太大动右。O (n) 时间、O (1) 空间,是后续三数之和、接雨水等题的基础。
题目 2:三数之和
题目分析
给你一个整数数组 nums,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、j != k 且 i != k,同时还满足 nums[i] + nums[j] + nums[k] == 0。请返回所有和为 0 且不重复 的三元组。 例:nums = [-1,0,1,2,-1,-4],答案 [[-1,-1,2],[-1,0,1]]。 核心:暴力 O (n³) 超时,排序后固定一个数,再用相向双指针 找剩下两数,可降到 O (n²)。难点在于去重:既要避免同一元素重复使用,又要保证答案三元组不重复。
算法原理
排序 + 固定一个数 + 双指针夹逼,核心步骤:
- 先对 nums 排序(有序才能用双指针,也方便去重)
- 外层循环固定第一个数
nums[i]:- 去重:若
i > 0且nums[i] == nums[i-1],跳过(同一数值只作为第一个数一次) - 设目标
target = -nums[i],用双指针在[i+1, n-1]里找两数之和等于 target
- 去重:若
- 内层相向双指针
left = i+1, right = n-1:sum = nums[left] + nums[right]sum == target:记录三元组,然后双指针同时去重(跳过重复的 left 和 right),再收缩sum < target:left++sum > target:right--
- 剪枝:
nums[i] > 0时后面全是正数,直接 break
去重是本题灵魂:固定数去重 + 找到答案后左右指针去重,三处去重缺一不可。
代码编写
cpp
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
sort(nums.begin(), nums.end());
int n = nums.size();
for(int i = 0; i < n - 2; i++) {
if(nums[i] > 0) break; // 剪枝:首项>0后面全正
if(i > 0 && nums[i] == nums[i - 1]) continue; // 去重固定数
int target = -nums[i];
int left = i + 1, right = n - 1;
while(left < right) {
int sum = nums[left] + nums[right];
if(sum == target) {
res.push_back({nums[i], nums[left], nums[right]});
while(left < right && nums[left] == nums[left + 1]) left++; // 去重左
while(left < right && nums[right] == nums[right - 1]) right--; // 去重右
left++;
right--;
} else if(sum < target) {
left++;
} else {
right--;
}
}
}
return res;
}
总结
三数之和 = 排序 + 固定一个 + 相向双指针,O (n²)。三个去重点(固定数、找到答案后的左右指针)是防重复的关键。它是「两数之和 II」的进阶,也是面试中出现频率极高的经典题。
本篇总结
本篇进入双指针系列,核心是相向双指针(有序数组两端夹逼):
- 两数之和 II:最简相向双指针,和的大小驱动指针单向移动
- 三数之和:排序后固定一个数,转成两数之和,难点是三层去重
相向双指针的价值:把有序数组的 O (n²) 暴力降到 O (n),本质是利用「单调性」排除不可能的组合。下一篇可以安排同类型进阶:最接近的三数之和 / 四数之和,或容器盛水最多的水 / 接雨水。
