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 数之和」的题都能套用模板解决。

相关推荐
20130924162716 小时前
1968年 Hart, Nilsson, Raphael 《最小成本路径启发式确定的形式基础》A* 算法深度研究报告
人工智能·算法
如何原谅奋力过但无声16 小时前
【力扣-Python-滑动窗口经典题】567.字符串的排列 | 424.替换后的最长重复字符 | 76.最小覆盖子串
算法·leetcode
玄冥剑尊17 小时前
贪心算法进阶
算法·贪心算法
玄冥剑尊17 小时前
贪心算法深化 I
算法·贪心算法
52Hz11817 小时前
力扣73.矩阵置零、54.螺旋矩阵、48.旋转图像
python·算法·leetcode·矩阵
BHXDML17 小时前
第一章:线性回归& 逻辑回归
算法·逻辑回归·线性回归
iAkuya18 小时前
(leetcode)力扣100 二叉搜索树种第K小的元素(中序遍历||记录子树的节点数)
算法·leetcode·职场和发展
Remember_99319 小时前
【LeetCode精选算法】滑动窗口专题二
java·开发语言·数据结构·算法·leetcode
Gorgous—l20 小时前
数据结构算法学习:LeetCode热题100-动态规划篇(下)(单词拆分、最长递增子序列、乘积最大子数组、分割等和子集、最长有效括号)
数据结构·学习·算法
北京地铁1号线20 小时前
2.3 相似度算法详解:Cosine Similarity 与 Euclidean Distance
算法·余弦相似度