LeetCode100天Day11-两数之和与合并两个有序数组

LeetCode100天Day11-两数之和与合并两个有序数组:暴力搜索与双指针合并

摘要:本文详细解析了LeetCode中两道经典数组题目------"两数之和"和"合并两个有序数组"。通过暴力查找解决两数之和问题,以及使用双指针合并有序数组,帮助读者掌握数组操作的基础技巧。

目录

文章目录

  • LeetCode100天Day11-两数之和与合并两个有序数组:暴力搜索与双指针合并
    • 目录
    • [1. 两数之和(Two Sum)](#1. 两数之和(Two Sum))
      • [1.1 题目描述](#1.1 题目描述)
      • [1.2 解题思路](#1.2 解题思路)
      • [1.3 代码实现](#1.3 代码实现)
      • [1.4 代码逐行解释](#1.4 代码逐行解释)
      • [1.5 执行流程详解](#1.5 执行流程详解)
      • [1.6 算法图解](#1.6 算法图解)
      • [1.7 复杂度分析](#1.7 复杂度分析)
      • [1.8 边界情况](#1.8 边界情况)
    • [2. 合并两个有序数组(Merge Sorted Array)](#2. 合并两个有序数组(Merge Sorted Array))
    • [3. 两题对比与总结](#3. 两题对比与总结)
      • [3.1 算法对比](#3.1 算法对比)
      • [3.2 双指针模板](#3.2 双指针模板)
      • [3.3 暴力枚举 vs 双指针](#3.3 暴力枚举 vs 双指针)
      • [3.4 数组克隆的注意事项](#3.4 数组克隆的注意事项)
    • [4. 总结](#4. 总结)
    • 参考资源
    • 文章标签

1. 两数之和(Two Sum)

1.1 题目描述

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。你可以按任意顺序返回答案。

示例 1

复制代码
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9,返回 [0, 1]

示例 2

复制代码
输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3

复制代码
输入:nums = [3,3], target = 6
输出:[0,1]

1.2 解题思路

这道题使用暴力枚举的方法:

  1. 使用两层循环遍历数组
  2. 外层循环固定第一个数
  3. 内层循环寻找第二个数
  4. 找到满足条件的两个数后返回下标

解题步骤

  1. 创建长度为2的结果数组
  2. 外层循环遍历数组,索引为i
  3. 内层循环从i+1开始,索引为j
  4. 检查nums[i] + nums[j]是否等于target
  5. 如果相等,保存下标并返回

1.3 代码实现

java 复制代码
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int answer[] = new int[2];
        for(int i = 0;i < nums.length;i++){
            for(int j = i+1;j < nums.length;j++){
                if(nums[i] + nums[j] == target){
                    answer[0] = i;
                    answer[1]  = j;
                    return answer;
                }
            }
        }
        return answer;
    }
}

1.4 代码逐行解释

第一部分:创建结果数组
java 复制代码
int answer[] = new int[2];

功能

  • 创建长度为2的数组,用于存储答案
  • answer[0] 存储第一个数的下标
  • answer[1] 存储第二个数的下标
第二部分:双层循环
java 复制代码
for(int i = 0;i < nums.length;i++){
    for(int j = i+1;j < nums.length;j++){
        if(nums[i] + nums[j] == target){
            answer[0] = i;
            answer[1]  = j;
            return answer;
        }
    }
}

循环结构

循环 变量 起始值 作用
外层 i 0 固定第一个数
内层 j i+1 寻找第二个数

为什么j从i+1开始

java 复制代码
// 避免重复检查
// 例如:nums = [2, 7, 11, 15], target = 9

// 如果j从0开始:
i=0, j=0: nums[0]+nums[0] = 2+2 = 4 (同一个元素,不符合题意)
i=0, j=1: nums[0]+nums[1] = 2+7 = 9 ✓
i=1, j=0: nums[1]+nums[0] = 7+2 = 9 (重复检查)
i=1, j=1: nums[1]+nums[1] = 7+7 = 14 (同一个元素)
...

// j从i+1开始:
i=0, j=1: nums[0]+nums[1] = 2+7 = 9 ✓ (找到答案)
i=0, j=2: nums[0]+nums[2] = 2+11 = 13
i=0, j=3: nums[0]+nums[3] = 2+15 = 17
i=1, j=2: nums[1]+nums[2] = 7+11 = 18
...

1.5 执行流程详解

示例1nums = [2,7,11,15], target = 9

复制代码
初始状态:
answer = [0, 0]
nums = [2, 7, 11, 15]
target = 9

i=0, nums[i]=2:
  j=1, nums[j]=7:
    nums[0] + nums[1] = 2 + 7 = 9
    9 == 9? 是,找到答案
    answer[0] = 0
    answer[1] = 1
    返回 [0, 1]

输出: [0, 1]

示例2nums = [3,2,4], target = 6

复制代码
i=0, nums[i]=3:
  j=1, nums[j]=2:
    3 + 2 = 5 != 6
  j=2, nums[j]=4:
    3 + 4 = 7 != 6

i=1, nums[i]=2:
  j=2, nums[j]=4:
    2 + 4 = 6 == 6? 是,找到答案
    answer[0] = 1
    answer[1] = 2
    返回 [1, 2]

输出: [1, 2]

示例3nums = [3,3], target = 6

复制代码
i=0, nums[i]=3:
  j=1, nums[j]=3:
    nums[0] + nums[1] = 3 + 3 = 6
    6 == 6? 是,找到答案
    answer[0] = 0
    answer[1] = 1
    返回 [0, 1]

输出: [0, 1]

1.6 算法图解

复制代码
nums = [2, 7, 11, 15]
target = 9

步骤1: i=0, 固定nums[0]=2
数组: [2,  7, 11, 15]
索引:  ↑
      i=0

      检查: nums[0] + nums[1] = 2 + 7 = 9 ✓

步骤2: j从i+1=1开始
数组: [2,  7, 11, 15]
索引:  ↑   ↑
      i=0 j=1

      2 + 7 = 9 == target ✓

结果: [0, 1]

nums = [3, 2, 4]
target = 6

步骤1: i=0, nums[0]=3
数组: [3,  2,  4]
索引:  ↑   ↑   ↑
      i=0 j=1 j=2

      j=1: 3 + 2 = 5 != 6
      j=2: 3 + 4 = 7 != 6

步骤2: i=1, nums[1]=2
数组: [3,  2,  4]
索引:       ↑   ↑
          i=1 j=2

      j=2: 2 + 4 = 6 == 6 ✓

结果: [1, 2]

1.7 复杂度分析

分析维度 复杂度 说明
时间复杂度 O(n²) 两层循环
空间复杂度 O(1) 只使用常数空间

优化思路:可以使用HashMap将时间复杂度降到O(n)

java 复制代码
// 优化版本:使用HashMap
class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for(int i = 0; i < nums.length; i++){
            int complement = target - nums[i];
            if(map.containsKey(complement)){
                return new int[]{map.get(complement), i};
            }
            map.put(nums[i], i);
        }
        return new int[]{};
    }
}

1.8 边界情况

nums target 说明 输出
[2,7] 9 最小数组 [0,1]
[3,3] 6 相同元素 [0,1]
[3,2,4] 6 不相邻 [1,2]

2. 合并两个有序数组(Merge Sorted Array)

2.1 题目描述

给你两个按 非递减顺序 排列的整数数组 nums1nums2,另有两个整数 mn,分别表示 nums1nums2 中的元素数目。

请你 合并 nums2 到 nums1 中 ,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0,应忽略。nums2 的长度为 n

示例 1

复制代码
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6],合并结果是 [1,2,2,3,5,6]

示例 2

复制代码
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]

示例 3

复制代码
输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]

2.2 解题思路

这道题使用双指针合并的方法:

  1. 克隆nums1作为备份
  2. 使用双指针分别遍历nums3和nums2
  3. 比较两个指针指向的元素,将较小的放入nums1
  4. 处理剩余元素

解题步骤

  1. 克隆nums1到nums3作为备份
  2. 创建三个指针:index(结果位置), index1(nums3), index2(nums2)
  3. 比较nums3[index1]和nums2[index2],小的放入nums1[index]
  4. 移动相应指针,直到一个数组遍历完
  5. 处理剩余元素

2.3 代码实现

java 复制代码
class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int index = 0;
        int[] nums3 = nums1.clone();
        int index1 = 0;
        int index2 = 0;
        while (index1 != n && index2 != m) {
            if (nums3[index2] > nums2[index1]) {
                nums1[index] = nums2[index1];
                index++;
                index1++;
            } else {
                nums1[index] = nums3[index2];
                index++;
                index2++;
            }
        }
        for (; index < m + n && index1 < n; index++) {
            nums1[index] = nums2[index1];
            index1++;
        }
        for (; index < m + n; index++) {
            nums1[index] = nums3[index2];
            index2++;
        }
    }
}

2.4 代码逐行解释

第一部分:初始化
java 复制代码
int index = 0;
int[] nums3 = nums1.clone();
int index1 = 0;
int index2 = 0;

变量说明

变量 作用 指向
index nums1的写入位置 从0开始
nums3 nums1的备份 nums1的前m个元素
index1 nums2的读取位置 从0开始
index2 nums3的读取位置 从0开始

为什么要克隆nums1

java 复制代码
nums1 = [1, 2, 3, 0, 0, 0]
m = 3, n = 3

nums2 = [2, 5, 6]

// 如果直接使用nums1:
// nums1[0] = min(nums1[0], nums2[0])
// 但nums1[0]会被覆盖,原值丢失

// 克隆后:
nums3 = [1, 2, 3]
// nums3保留原值,nums1可以安全修改
第二部分:双指针合并
java 复制代码
while (index1 != n && index2 != m) {
    if (nums3[index2] > nums2[index1]) {
        nums1[index] = nums2[index1];
        index++;
        index1++;
    } else {
        nums1[index] = nums3[index2];
        index++;
        index2++;
    }
}

合并逻辑

条件 操作 说明
nums3[index2] > nums2[index1] 取nums2的元素 nums2的更小
否则 取nums3的元素 nums3的更小或相等

注意 :条件中有个bug,应该是 index1 < n 而不是 index1 != n,但逻辑上两者等价(因为index1只增不减)

第三部分:处理剩余元素
java 复制代码
for (; index < m + n && index1 < n; index++) {
    nums1[index] = nums2[index1];
    index1++;
}
for (; index < m + n; index++) {
    nums1[index] = nums3[index2];
    index2++;
}

处理逻辑

循环 条件 操作
第一个 index1 < n nums2还有剩余元素
第二个 index < m + n nums3还有剩余元素

2.5 执行流程详解

示例1nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3

复制代码
初始状态:
nums1 = [1, 2, 3, 0, 0, 0]
nums2 = [2, 5, 6]
nums3 = [1, 2, 3]
index = 0, index1 = 0, index2 = 0

步骤1: index=0, index1=0, index2=0
nums3[0]=1, nums2[0]=2
1 > 2? 否
nums1[0] = nums3[0] = 1
index = 1, index2 = 1
nums1 = [1, 2, 3, 0, 0, 0]

步骤2: index=1, index1=0, index2=1
nums3[1]=2, nums2[0]=2
2 > 2? 否
nums1[1] = nums3[1] = 2
index = 2, index2 = 2
nums1 = [1, 2, 3, 0, 0, 0]

步骤3: index=2, index1=0, index2=2
nums3[2]=3, nums2[0]=2
3 > 2? 是
nums1[2] = nums2[0] = 2
index = 3, index1 = 1
nums1 = [1, 2, 2, 0, 0, 0]

步骤4: index=3, index1=1, index2=2
nums3[2]=3, nums2[1]=5
3 > 5? 否
nums1[3] = nums3[2] = 3
index = 4, index2 = 3
index2 == m,退出循环

处理剩余:
index1=1 < 3, nums2还有元素
nums1[4] = nums2[1] = 5
index1=2, index=5

nums1[5] = nums2[2] = 6
index1=3, index=6

最终输出: [1, 2, 2, 3, 5, 6]

示例2nums1 = [1], m = 1, nums2 = [], n = 0

复制代码
初始状态:
nums1 = [1]
nums2 = []
nums3 = [1]
index = 0, index1 = 0, index2 = 0

while循环:index1=0, index2=0
index1 != n(0)? 否,不进入循环

处理剩余:
index1 < n? 0 < 0? 否
index < m + n? 0 < 1? 是
nums1[0] = nums3[0] = 1

最终输出: [1]

示例3nums1 = [0], m = 0, nums2 = [1], n = 1

复制代码
初始状态:
nums1 = [0]
nums2 = [1]
nums3 = [0] (但m=0,不使用)
index = 0, index1 = 0, index2 = 0

while循环:index1=0, index2=0
index2 != m(0)? 否,不进入循环

处理剩余:
index1 < n? 0 < 1? 是
nums1[0] = nums2[0] = 1
index1 = 1, index = 1

最终输出: [1]

2.6 算法图解

复制代码
nums1 = [1, 2, 3, 0, 0, 0]
nums2 = [2, 5, 6]

步骤1:
nums3: [1, 2, 3]
         ↑
      index2=0

nums2: [2, 5, 6]
        ↑
     index1=0

比较: 1 vs 2
取1,放入nums1[0]

nums1: [1, 2, 3, 0, 0, 0]
        ↑
     index=0→1

步骤2:
nums3: [1, 2, 3]
            ↑
         index2=1

nums2: [2, 5, 6]
        ↑
     index1=0

比较: 2 vs 2
取nums3的2,放入nums1[1]

nums1: [1, 2, 3, 0, 0, 0]
           ↑
        index=1→2

步骤3:
nums3: [1, 2, 3]
            ↑
         index2=2

nums2: [2, 5, 6]
        ↑
     index1=0

比较: 3 vs 2
取nums2的2,放入nums1[2]

nums1: [1, 2, 2, 0, 0, 0]
              ↑
           index=2→3

继续...直到合并完成

最终结果:
nums1: [1, 2, 2, 3, 5, 6]

2.7 复杂度分析

分析维度 复杂度 说明
时间复杂度 O(m + n) 遍历两个数组
空间复杂度 O(m) 克隆nums1

优化思路:可以从后向前合并,避免克隆

java 复制代码
// 优化版本:从后向前
class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int p1 = m - 1;
        int p2 = n - 1;
        int p = m + n - 1;

        while (p1 >= 0 && p2 >= 0) {
            if (nums1[p1] > nums2[p2]) {
                nums1[p--] = nums1[p1--];
            } else {
                nums1[p--] = nums2[p2--];
            }
        }

        while (p2 >= 0) {
            nums1[p--] = nums2[p2--];
        }
    }
}

2.8 边界情况

nums1 m nums2 n 说明 输出
[1] 1 [] 0 nums2为空 [1]
[0] 0 [1] 1 nums1为空 [1]
[1,2,3,0,0,0] 3 [2,5,6] 3 正常情况 [1,2,2,3,5,6]

3. 两题对比与总结

3.1 算法对比

对比项 两数之和 合并两个有序数组
核心算法 暴力枚举 双指针合并
数据结构 数组 数组
时间复杂度 O(n²) O(m + n)
空间复杂度 O(1) O(m)
应用场景 查找问题 合并问题

3.2 双指针模板

java 复制代码
// 双指针合并模板
int i = 0;  // 数组1的指针
int j = 0;  // 数组2的指针
int k = 0;  // 结果数组的指针

while (i < len1 && j < len2) {
    if (array1[i] < array2[j]) {
        result[k++] = array1[i++];
    } else {
        result[k++] = array2[j++];
    }
}

// 处理剩余元素
while (i < len1) {
    result[k++] = array1[i++];
}
while (j < len2) {
    result[k++] = array2[j++];
}

3.3 暴力枚举 vs 双指针

对比项 暴力枚举 双指针
时间复杂度 O(n²) O(n)
适用场景 无序数组 有序数组
实现难度 简单 中等
空间效率

3.4 数组克隆的注意事项

java 复制代码
// 浅克隆 vs 深克隆
int[] arr1 = {1, 2, 3};
int[] arr2 = arr1.clone();  // 浅克隆,对于基本类型足够

int[][] arr3 = {{1, 2}, {3, 4}};
int[][] arr4 = arr3.clone();  // 浅克隆,子数组共享引用

// 对于二维数组,需要深克隆
int[][] arr5 = new int[arr3.length][];
for (int i = 0; i < arr3.length; i++) {
    arr5[i] = arr3[i].clone();
}

4. 总结

今天我们学习了两道数组基础题目:

  1. 两数之和:掌握暴力枚举的思路,理解双层循环的使用
  2. 合并两个有序数组:掌握双指针合并技巧,理解有序数组的处理方法

核心收获

  • 暴力枚举是最直接的解决方法,虽然效率不高但容易理解
  • 双指针是处理有序数组的高效方法
  • 合并问题需要处理剩余元素
  • 数组克隆可以在修改前保留原始数据

练习建议

  1. 尝试用HashMap优化两数之和
  2. 尝试从后向前合并两个有序数组(避免克隆)
  3. 思考如何合并多个有序数组

参考资源

文章标签

#LeetCode #算法 #Java #数组 #双指针

喜欢这篇文章吗?别忘了点赞、收藏和分享!你的支持是我创作的最大动力!

相关推荐
leiming62 分钟前
c++ find 算法
算法
CoovallyAIHub4 分钟前
YOLOv12之后,AI在火场如何进化?2025最后一篇YOLO论文揭示:要在浓烟中看见关键,仅靠注意力还不够
深度学习·算法·计算机视觉
梭七y4 分钟前
【力扣hot100题】(121)反转链表
算法·leetcode·链表
qq_433554546 分钟前
C++字符串hash
c++·算法·哈希算法
无限进步_7 分钟前
【C语言】堆(Heap)的数据结构与实现:从构建到应用
c语言·数据结构·c++·后端·其他·算法·visual studio
再难也得平7 分钟前
两数之和和字母异位词分组
数据结构·算法
黎雁·泠崖17 分钟前
【线性表系列入门篇】从顺序表到链表:解锁数据结构的进化密码
c语言·数据结构·链表
shizhenshide21 分钟前
极速响应:平均破解速度<3秒的验证码服务,为抢购爬虫而生
算法
AD钙奶-lalala21 分钟前
leetcode核心母题总结
算法·leetcode·职场和发展
YGGP34 分钟前
【Golang】LeetCode 2. 两数相加
开发语言·leetcode·golang