LeetCode 167/15 两数之和与三数之和-双指针1

一、两数之和 II - 输入有序数组(LeetCode 167)

题目描述

给你一个 非递减排序 的整数数组 numbers,请你从数组中找出两个数,使它们的和等于目标数 target

  • 函数应该以长度为 2 的整数数组的形式返回这两个数的下标值(索引从 1 开始)。
  • 输入有且只有一个解,且同一个元素不能被重复使用。

完整代码

java 复制代码
class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int n = numbers.length;
        // 1. 指针初始化:左指针头(0)、右指针尾(n-1)(对应之前的解题重点)
        int left = 0;
        int right = n - 1;

        // 2. 双指针核心逻辑:left < right 避免指针交叉(剪枝基础)
        while (left < right) {
            int sum = numbers[left] + numbers[right];
            
            // 剪枝:提前终止无效循环(可选,但提升效率)
            // 若左指针元素 > target/2,后续和必然超过target(有序数组)
            if (numbers[left] > target / 2) {
                break;
            }

            if (sum > target) {
                // 和太大:右指针左移(和变小,对应解题重点的移动逻辑)
                right--;
            } else if (sum < target) {
                // 和太小:左指针右移(和变大)
                left++;
            } else {
                // 3. 坑点:索引从1开始,返回[left+1, right+1]
                return new int[]{left + 1, right + 1};
            }
        }
        // 题目保证有唯一解,此处仅为语法兜底
        return new int[]{-1, -1};
    }
}

代码关键解释

  1. 指针初始化:严格对应「左头右尾」,利用有序数组的单调性;
  2. 剪枝优化numbers[left] > target/2 时,后续和必然超过 target(因为数组递增,right≥left,numbers [right]≥numbers [left]),直接 break;
  3. 索引坑点 :最终返回left+1/right+1,而非原数组的 0 开始索引;
  4. 时间 / 空间复杂度:O (n) / O (1),满足题目「常量级额外空间」要求。

二、三数之和(LeetCode 15)

题目描述

给你一个整数数组 nums,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j != k,且 nums[i] + nums[j] + nums[k] = 0

  • 要求:返回所有和为 0 且不重复的三元组(不能有重复的组合)。
  • 数组可能包含重复元素,需手动去重。

完整代码

java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        int n = nums.length;
        if (n < 3) {
            return res; // 剪枝:元素不足3个,直接返回空
        }

        // 1. 先排序(核心前提:有序才能双指针+剪枝+去重)
        Arrays.sort(nums);

        // 2. 固定第一个数(k-2=1个固定数),转化为两数之和问题(对应解题模板)
        for (int i = 0; i < n - 2; i++) {
            // 剪枝1:固定数>0,后续和必然>0(有序数组),直接break
            if (nums[i] > 0) {
                break;
            }

            // 去重1:固定数去重(避免重复组合,对应去重规则)
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }

            // 3. 双指针初始化:left=i+1(避免重复使用i)、right=n-1
            int left = i + 1;
            int right = n - 1;
            while (left < right) { // 剪枝:指针不交叉
                int sum = nums[i] + nums[left] + nums[right];

                if (sum > 0) {
                    // 和太大:右指针左移(和变小)
                    right--;
                } else if (sum < 0) {
                    // 和太小:左指针右移(和变大)
                    left++;
                } else {
                    // 找到合法组合,加入结果
                    res.add(Arrays.asList(nums[i], nums[left], nums[right]));

                    // 去重2:左指针去重(跳过重复元素,避免重复组合)
                    while (left < right && nums[left] == nums[left + 1]) {
                        left++;
                    }
                    // 去重3:右指针去重
                    while (left < right && nums[right] == nums[right - 1]) {
                        right--;
                    }

                    // 4. 移动指针:找到解后,双指针同时收缩(避免死循环)
                    left++;
                    right--;
                }
            }
        }
        return res;
    }
}

代码关键解释(对应之前的剪枝 / 去重规则)

  1. 排序前提 :无序数组无法双指针 / 剪枝 / 去重,必须先Arrays.sort(nums)
  2. 剪枝逻辑
    • 元素不足 3 个:直接返回空(无效输入);
    • 固定数nums[i]>0:有序数组后续数更大,和不可能为 0,直接 break;
    • 指针交叉left >= right:终止内层循环(双指针基础剪枝);
  3. 去重逻辑
    • 固定数去重:i>0 && nums[i]==nums[i-1] → continue(跳过重复的固定数);
    • 左指针去重:找到解后,while(left<right && nums[left]==nums[left+1]) → left++;
    • 右指针去重:找到解后,while(left<right && nums[right]==nums[right-1]) → right--;
    • ❗ 关键:去重必须在「找到合法组合后」执行,且用left++/right--(不能用 continue,否则死循环);
  4. 核心转化 :固定i后,问题转化为「在[i+1, n-1]找两个数,和为-nums[i]」(两数之和的变种);
  5. 时间复杂度:O (n²)(排序 O (n log n) + 双层循环 O (n²)),空间复杂度 O (log n)(排序的系统栈空间)。

总结:两道题的核心关联与差异

维度 两数之和 II 三数之和
数组状态 输入已排序 需手动排序
双指针角色 直接头 + 尾双指针 外层固定 1 数,内层头 + 尾双指针
去重需求 无(输入唯一解) 必须去重(避免重复组合)
剪枝重点 左指针 > target/2 直接 break 固定数 > 0 直接 break
结果形式 返回索引(从 1 开始) 返回不重复的数值组合

两道题的核心思想完全一致:利用有序性 + 双指针缩小范围,区别仅在于「是否需要固定数」「是否需要去重」------ 掌握这个核心,所有「有序数组找 k 数之和」的题都能套用模板解决。

相关推荐
Hello娃的2 小时前
【神经网络】反向传播BP算法
人工智能·神经网络·算法
lynnlovemin2 小时前
从暴力到高效:C++ 算法优化实战 —— 排序与双指针篇
java·c++·算法
jinxinyuuuus2 小时前
快手在线去水印:短链解析、API逆向与视频流的元数据重构
前端·人工智能·算法·重构
Flash.kkl2 小时前
优先算法专题十五——BFS_FloodFill
算法·宽度优先
高洁013 小时前
向量数据库拥抱大模型
python·深度学习·算法·机器学习·transformer
慕容青峰3 小时前
牛客小白月赛 103 C 题题解
c++·算法·sublime text
小龙报3 小时前
【算法通关指南:算法基础篇(四)】二维差分专题:1.【模板】差分 2.地毯
c语言·数据结构·c++·深度学习·神经网络·算法·自然语言处理
立志成为大牛的小牛3 小时前
数据结构——五十八、希尔排序(Shell Sort)(王道408)
数据结构·学习·程序人生·考研·算法·排序算法