LeetCode100天Day7-移动零与搜索插入位置

LeetCode100天Day7-移动零与搜索插入位置:数组原位修改与查找操作

摘要:本文详细解析了LeetCode中两道数组操作题目------"移动零"和"搜索插入位置"。通过原位移位操作实现零元素移动,以及通过线性查找确定插入位置,帮助读者掌握数组的基础操作和边界处理技巧。

目录

文章目录

  • LeetCode100天Day7-移动零与搜索插入位置:数组原位修改与查找操作
    • 目录
    • [1. 移动零(Move Zeroes)](#1. 移动零(Move Zeroes))
    • [2. 搜索插入位置(Search Insert Position)](#2. 搜索插入位置(Search Insert Position))
      • [2.1 题目描述](#2.1 题目描述)
      • [2.2 解题思路](#2.2 解题思路)
      • [2.3 代码实现](#2.3 代码实现)
      • [2.4 代码逐行解释](#2.4 代码逐行解释)
      • [2.5 执行流程示例](#2.5 执行流程示例)
      • [2.6 算法图解](#2.6 算法图解)
      • [2.7 复杂度分析](#2.7 复杂度分析)
      • [2.8 边界情况](#2.8 边界情况)
    • [3. 两题对比与总结](#3. 两题对比与总结)
      • [3.1 算法对比](#3.1 算法对比)
      • [3.2 数组操作技巧总结](#3.2 数组操作技巧总结)
      • [3.3 循环控制技巧](#3.3 循环控制技巧)
    • [4. 总结](#4. 总结)
    • 参考资源
    • 文章标签

1. 移动零(Move Zeroes)

1.1 题目描述

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意,必须在不复制数组的情况下原地对数组进行操作。

示例 1

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

示例 2

复制代码
输入: nums = [0]
输出: [0]

1.2 解题思路

这道题要求在不复制数组的情况下原地操作,核心思想是:

  1. 遇到0时,将其后面的所有元素向前移动一位
  2. 将0放到数组末尾
  3. 缩小需要检查的范围

解题步骤

  1. 使用index标记当前数组末尾的有效位置
  2. 使用head指针遍历数组
  3. 遇到0时,将后面的元素全部前移,0放到末尾
  4. 更新index和head指针

1.3 代码实现

java 复制代码
class Solution {
    public void moveZeroes(int[] nums) {
        int index = nums.length - 1;
        int head = 0;
        int temp = 0;

        while(head < index){
            if(nums[head] == 0){
                // 将head之后的元素全部前移一位
                for(int i = head; i < index; i++){
                    nums[i] = nums[i + 1];
                }
                // 将0放到末尾
                nums[index] = 0;
                // 缩小范围
                index--;
                continue;
            }
            head++;
        }
    }
}

1.4 代码逐行解释

第一部分:变量初始化
java 复制代码
int index = nums.length - 1;  // 标记当前末尾位置
int head = 0;                 // 遍历指针
int temp = 0;                 // 未使用的临时变量

变量作用图示

复制代码
初始状态: nums = [0,1,0,3,12]

index: ──────────────→ 4 (末尾位置)
head:  → 0 (遍历指针)

数组: [0, 1, 0, 3, 12]
        ↑
       head=0
第二部分:主循环逻辑
java 复制代码
while(head < index){
    if(nums[head] == 0){
        // 找到0,执行移位操作
    } else {
        head++;  // 不是0,继续前进
    }
}

循环终止条件head < index

  • 当head与index相遇时,说明所有需要检查的位置都已处理完毕
第三部分:移位操作
java 复制代码
if(nums[head] == 0){
    // 步骤1:前移元素
    for(int i = head; i < index; i++){
        nums[i] = nums[i + 1];
    }
    // 步骤2:末尾填0
    nums[index] = 0;
    // 步骤3:缩小范围
    index--;
    // 步骤4:继续检查当前位置
    continue;
}

1.5 执行流程详解

示例nums = [0,1,0,3,12]

复制代码
初始状态:
  nums = [0, 1, 0, 3, 12]
  head = 0, index = 4

第1次循环 (head=0):
  nums[0] = 0,找到零
  前移:[1, 0, 3, 12, 12]
  末尾填0:[1, 0, 3, 12, 0]
  index-- → 3

第2次循环 (head=0):
  nums[0] = 1,不是0
  head++ → 1

第3次循环 (head=1):
  nums[1] = 0,找到零
  前移:[1, 3, 12, 12, 0]
  末尾填0:[1, 3, 12, 0, 0]
  index-- → 2

第4次循环 (head=1):
  nums[1] = 3,不是0
  head++ → 2

循环结束:head(2) >= index(2)

最终结果:[1, 3, 12, 0, 0]

1.6 算法图解

复制代码
原始数组: [0, 1, 0, 3, 12]
          ↓
发现0在位置0
          ↓
前移:     [1, 0, 3, 12, _]
填0:      [1, 0, 3, 12, 0]
          ↓
发现0在位置1
          ↓
前移:     [1, 3, 12, 0, _]
填0:      [1, 3, 12, 0, 0]
          ↓
完成

1.7 复杂度分析

分析维度 复杂度 说明
时间复杂度 O(n²) 最坏情况需要多次移动元素
空间复杂度 O(1) 原地操作,只用常数空间

1.8 边界情况

输入 说明 输出
[0] 单个0 [0]
[1] 单个非0 [1]
[0,0,0] 全是0 [0,0,0]
[1,2,3] 无0 [1,2,3]
[0,0,1] 前面有0 [1,0,0]

2. 搜索插入位置(Search Insert Position)

2.1 题目描述

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1

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

示例 2

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

示例 3

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

2.2 解题思路

虽然题目要求O(log n)(二分查找),但这里的解法使用线性查找。对于已排序的数组,从前往后遍历,找到第一个大于或等于target的位置即为答案。

解题思路

  1. 遍历数组
  2. 找到第一个大于或等于target的元素
  3. 返回该位置索引
  4. 如果没找到,返回数组长度(插入到末尾)

2.3 代码实现

java 复制代码
class Solution {
    public int searchInsert(int[] nums, int target) {
        for(int i = 0; i < nums.length; i++){
            if(nums[i] >= target){
                return i;
            }
        }
        return nums.length;
    }
}

2.4 代码逐行解释

第一部分:遍历查找
java 复制代码
for(int i = 0; i < nums.length; i++){
    if(nums[i] >= target){
        return i;
    }
}

查找逻辑

nums[i] target 条件 操作
1 5 1 >= 5? No 继续
3 5 3 >= 5? No 继续
5 5 5 >= 5? Yes 返回2
6 5 不执行 -
第二部分:未找到的处理
java 复制代码
return nums.length;

为什么返回length?

复制代码
nums = [1, 3, 5, 6], target = 7

遍历过程:
i=0: 1 >= 7? No
i=1: 3 >= 7? No
i=2: 5 >= 7? No
i=3: 6 >= 7? No

循环结束,所有元素都小于target
应该插入到位置4(数组末尾)

返回:nums.length = 4

2.5 执行流程示例

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

复制代码
i = 0: nums[0] = 1, 1 >= 5? 否,继续
i = 1: nums[1] = 3, 3 >= 5? 否,继续
i = 2: nums[2] = 5, 5 >= 5? 是,返回 2

输出: 2

示例2nums = [1,3,5,6], target = 2

复制代码
i = 0: nums[0] = 1, 1 >= 2? 否,继续
i = 1: nums[1] = 3, 3 >= 2? 是,返回 1

输出: 1

示例3nums = [1,3,5,6], target = 7

复制代码
i = 0: nums[0] = 1, 1 >= 7? 否,继续
i = 1: nums[1] = 3, 3 >= 7? 否,继续
i = 2: nums[2] = 5, 5 >= 7? 否,继续
i = 3: nums[3] = 6, 6 >= 7? 否,继续

循环结束,返回 nums.length = 4

输出: 4

2.6 算法图解

复制代码
情况1:目标值存在于数组中
nums = [1, 3, 5, 6], target = 5
              ↑
             返回2

情况2:目标值应插入中间
nums = [1, 3, 5, 6], target = 2
         ↑
        返回1

情况3:目标值应插入末尾
nums = [1, 3, 5, 6], target = 7
                       ↑
                      返回4

情况4:目标值应插入开头
nums = [1, 3, 5, 6], target = 0
      ↑
     返回0

2.7 复杂度分析

分析维度 复杂度 说明
时间复杂度 O(n) 线性查找,最坏遍历整个数组
空间复杂度 O(1) 只使用常数空间

注意:题目要求O(log n),应该使用二分查找。但提供的解法是线性查找,虽然复杂度不满足要求,但对于小规模数据仍然有效。

2.8 边界情况

nums target 说明 输出
[1] 1 找到目标 0
[1] 0 插入开头 0
[1] 2 插入末尾 1
[1,3] 2 插入中间 1
[] 1 空数组 0

3. 两题对比与总结

3.1 算法对比

对比项 移动零 搜索插入位置
操作类型 原地修改 查找
核心技巧 元素移位 顺序查找
时间复杂度 O(n²) O(n)
空间复杂度 O(1) O(1)
数组状态 任意 已排序

3.2 数组操作技巧总结

原地修改技巧

java 复制代码
// 1. 元素前移
for(int i = pos; i < length - 1; i++){
    array[i] = array[i + 1];
}

// 2. 元素后移
for(int i = length - 1; i > pos; i--){
    array[i] = array[i - 1];
}

// 3. 交换元素
int temp = array[i];
array[i] = array[j];
array[j] = temp;

查找技巧

java 复制代码
// 1. 顺序查找
for(int i = 0; i < array.length; i++){
    if(array[i] == target){
        return i;
    }
}

// 2. 条件查找
for(int i = 0; i < array.length; i++){
    if(array[i] >= target){
        return i;
    }
}

3.3 循环控制技巧

continue的使用

java 复制代码
while(condition){
    if(某个条件){
        // 执行操作
        continue;  // 跳过后续代码,继续循环
    }
    // 其他操作
}

在移动零中,使用continue是为了:

  • 找到0并处理后,继续检查当前位置
  • 因为0被后面的元素替换,需要再次检查

4. 总结

今天我们学习了两道数组操作题目:

  1. 移动零:掌握原地修改数组的技巧,通过元素移位实现需求
  2. 搜索插入位置:掌握在有序数组中查找插入位置的方法

核心收获

  • 原地修改数组是常见面试考点
  • 元素移位操作需要仔细处理边界
  • 查找插入位置要考虑各种情况
  • continue语句可以控制循环流程

思考题

  1. 移动零能否用O(n)时间复杂度实现?(提示:双指针)
  2. 搜索插入位置如何用二分查找实现O(log n)?
  3. 如果数组未排序,搜索插入位置该如何处理?

参考资源

文章标签

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

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

相关推荐
ullio2 小时前
div1+2. 2178E - Flatten or Concatenate
算法
yu_anan1112 小时前
PPO/GRPO算法在RLHF中的实现
算法
leoufung2 小时前
Word Break:深度理解 DP 前缀结束点的核心思想
算法·word·动态规划
Aaron15882 小时前
三种主流接收机架构(超外差、零中频、射频直采)对比及发展趋势浅析
c语言·人工智能·算法·fpga开发·架构·硬件架构·信号处理
草莓熊Lotso3 小时前
Python 进阶核心:字典 / 文件操作 + 上下文管理器实战指南
数据结构·c++·人工智能·经验分享·笔记·git·python
乐迪信息5 小时前
乐迪信息:目标检测算法+AI摄像机:煤矿全场景识别方案
人工智能·物联网·算法·目标检测·目标跟踪·语音识别
前端小L11 小时前
贪心算法专题(十):维度权衡的艺术——「根据身高重建队列」
javascript·算法·贪心算法
方得一笔11 小时前
自定义常用的字符串函数(strlen,strcpy,strcmp,strcat)
算法
Xの哲學11 小时前
Linux SMP 实现机制深度剖析
linux·服务器·网络·算法·边缘计算