【算法拾光 7】双指针|两数之和 II - 输入有序数组 + 三数之和

题目 1:两数之和 II - 输入有序数组

链接:167. 两数之和 II - 输入有序数组 - 力扣(LeetCode)

题目分析

给你一个下标从 1 开始的整数数组 numbers,该数组已按非递减顺序排列 。请你从数组中找出满足相加之和等于目标数 target 的两个数。返回这两个数对应的下标(下标从 1 开始),且每个输入只有一个答案,不能重复使用同一个元素。 例:numbers = [2,7,11,15], target = 9,答案 [1,2]。 核心:数组有序 + 唯一解,适合用相向双指针,左右两端夹逼,O (n) 解决,比哈希表更省空间、更能体现有序价值。

算法原理

利用有序数组「两端夹逼」的单调性:

  1. 左指针 left = 0,右指针 right = n-1
  2. 计算 sum = numbers[left] + numbers[right]
    • sum == target:找到答案,返回 [left+1, right+1]
    • sum < target:和太小,需要更大的数,左指针右移 → left++
    • sum > target:和太大,需要更小的数,右指针左移 → right--
  3. 直到 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:三数之和

链接:15. 三数之和 - 力扣(LeetCode)

题目分析

给你一个整数数组 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²)。难点在于去重:既要避免同一元素重复使用,又要保证答案三元组不重复。

算法原理

排序 + 固定一个数 + 双指针夹逼,核心步骤:

  1. 先对 nums 排序(有序才能用双指针,也方便去重)
  2. 外层循环固定第一个数 nums[i]:
    • 去重:若 i > 0 且 nums[i] == nums[i-1],跳过(同一数值只作为第一个数一次)
    • 设目标 target = -nums[i],用双指针在 [i+1, n-1] 里找两数之和等于 target
  3. 内层相向双指针 left = i+1, right = n-1:
    • sum = nums[left] + nums[right]
    • sum == target:记录三元组,然后双指针同时去重(跳过重复的 left 和 right),再收缩
    • sum < target:left++
    • sum > target:right--
  4. 剪枝: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」的进阶,也是面试中出现频率极高的经典题。


本篇总结

本篇进入双指针系列,核心是相向双指针(有序数组两端夹逼):

  1. 两数之和 II:最简相向双指针,和的大小驱动指针单向移动
  2. 三数之和:排序后固定一个数,转成两数之和,难点是三层去重

相向双指针的价值:把有序数组的 O (n²) 暴力降到 O (n),本质是利用「单调性」排除不可能的组合。下一篇可以安排同类型进阶:最接近的三数之和 / 四数之和,或容器盛水最多的水 / 接雨水。

相关推荐
Navigator_Z2 小时前
LeetCode //C - 1304. Find N Unique Integers Sum up to Zero
c语言·算法·leetcode
企业数字化笔记13 小时前
视频人体姿态怎么做三维可视化?2D关键点、深度局限与技术选型
人工智能·算法·音视频
?abc!13 小时前
(数组)581. 最短无序连续子数组
数据结构·算法·leetcode
小郎君65816 小时前
MPTT树复选框的半选状态:一棵百万节点树从递归到计数器,再到区间覆盖
算法
酌沧16 小时前
MemT让记忆成为模型内生能力
jvm·人工智能·算法
Shimeri17 小时前
越卷越像:我用遗传算法做了个"内卷"实验
算法·three.js·数据可视化·遗传算法·进化算法
Evand J17 小时前
【PMSM例程1】永磁同步电机(PMSM)转速与转子电角度观测算法|基于扩展卡尔曼滤波(EKF)的传感器状态估计与误差。附下载链接
算法·电机·1024程序员节·卡尔曼滤波
guo_wen_qiang17 小时前
二叉树的层序遍历-使用双端队列Deque
算法·leetcode·1024程序员节