LeetCode100天Day7-移动零与搜索插入位置:数组原位修改与查找操作
摘要:本文详细解析了LeetCode中两道数组操作题目------"移动零"和"搜索插入位置"。通过原位移位操作实现零元素移动,以及通过线性查找确定插入位置,帮助读者掌握数组的基础操作和边界处理技巧。
目录
文章目录
- LeetCode100天Day7-移动零与搜索插入位置:数组原位修改与查找操作
-
- 目录
- [1. 移动零(Move Zeroes)](#1. 移动零(Move Zeroes))
-
- [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. 搜索插入位置(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 解题思路
这道题要求在不复制数组的情况下原地操作,核心思想是:
- 遇到0时,将其后面的所有元素向前移动一位
- 将0放到数组末尾
- 缩小需要检查的范围
解题步骤:
- 使用index标记当前数组末尾的有效位置
- 使用head指针遍历数组
- 遇到0时,将后面的元素全部前移,0放到末尾
- 更新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的位置即为答案。
解题思路:
- 遍历数组
- 找到第一个大于或等于target的元素
- 返回该位置索引
- 如果没找到,返回数组长度(插入到末尾)
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 执行流程示例
示例1 :nums = [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
示例2 :nums = [1,3,5,6], target = 2
i = 0: nums[0] = 1, 1 >= 2? 否,继续
i = 1: nums[1] = 3, 3 >= 2? 是,返回 1
输出: 1
示例3 :nums = [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. 总结
今天我们学习了两道数组操作题目:
- 移动零:掌握原地修改数组的技巧,通过元素移位实现需求
- 搜索插入位置:掌握在有序数组中查找插入位置的方法
核心收获:
- 原地修改数组是常见面试考点
- 元素移位操作需要仔细处理边界
- 查找插入位置要考虑各种情况
- continue语句可以控制循环流程
思考题:
- 移动零能否用O(n)时间复杂度实现?(提示:双指针)
- 搜索插入位置如何用二分查找实现O(log n)?
- 如果数组未排序,搜索插入位置该如何处理?
参考资源
文章标签
#LeetCode #算法 #Java #数组 #双指针
喜欢这篇文章吗?别忘了点赞、收藏和分享!你的支持是我创作的最大动力!