https://leetcode.cn/problems/move-zeroes/description/
一、题目描述
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
今天的题目解题方法还是非常多的,我刚看到题目的时候本来想说用开辟一个新数组的方式来做这道题,但是题目要求必须在不复制数组的情况下原地对数组进行操作。不过不重要,条条大路通罗马嘛。(这里我就选择两种比较容易理解的方法分享给大家)
二、示例分析
示例 1:
输入: nums =[0,1,0,3,12]
输出:[1,3,12,0,0]
示例 2:
输入: nums =[0]
输出:[0]
通过示例,简单来说就是把为0的元素统统放到数组的右边,不为0的元素统统放到数组的左边。
注意:本题有坑,但不大。再不为0的元素移动时记得要保持原本位置不变哦!
假设本题的数组元素为[0,1,0,3,2],那么最终的答案应该为[1,3,2,0,0];并非是从大到小排序!
三、解题思路&代码实现
题解一:
通过实例分析,我的初步想法是先判断数组前后的元素是否为0,如果第一个元素为0,后一个元素不为0,那么我们需要把这两个元素进行交换。
// 循环遍历数组nums,i从0开始,只要i + 1小于数组大小numsSize就继续循环
for (int i = 0; i + 1 < numsSize; i++) {
// 检查当前元素nums[i]是否为0,且下一个元素nums[i + 1]不为0
if (nums[i] == 0 && nums[i + 1]!= 0) {
//一下三行为交换i和i+1的位置
int t = nums[i];
nums[i]=nums[i+1];
nums[i+1]=t;
}
}
那么解决了这一步,我们才刚刚解决了整理一次之后的结果,现在我们需要在for循环的外面再加上一个外层循环。
while(true)
{
// 循环遍历数组nums,i从0开始,只要i + 1小于数组大小numsSize就继续循环
for (int i = 0; i + 1 < numsSize; i++) {
// 检查当前元素nums[i]是否为0,且下一个元素nums[i + 1]不为0
if (nums[i] == 0 && nums[i + 1]!= 0) {
//一下三行为交换i和i+1的位置
int t = nums[i];
nums[i]=nums[i+1];
nums[i+1]=t;
}
}
}
嗯......循环是加上了,但是这里很多小伙伴应该一眼就能看出来,这不是死循环了吗?对,这就是死循环,完成题目我们还需要加上一个标志位,用来判断是否已经将数组里的所有为0的元素移动完毕。
// 无限循环,直到满足内部的退出条件才会跳出
while (true) {
// 初始化一个标志变量flag为0,用于记录是否进行了交换操作
int flag = 0;
// 循环遍历数组nums,i从0开始,只要i + 1小于数组大小numsSize就继续循环
for (int i = 0; i + 1 < numsSize; i++) {
// 检查当前元素nums[i]是否为0,且下一个元素nums[i + 1]不为0
if (nums[i] == 0 && nums[i + 1]!= 0) {
// 如果满足条件,将flag加1,表示进行了一次交换操作
flag++;
int t = nums[i];
nums[i]=nums[i+1];
nums[i+1]=t;
}
}
// 如果在一次完整的数组遍历中没有进行任何交换操作(flag为0),则跳出外部的while循环
if (flag == 0)
break;
}
这样题解1就顺利跑通了。
题解二:
题解二相对题解一来说,我个人是要感觉简单很多的,可以理解为直接写答案了。
整体的思路类似我发布的LeetCode算法题day1https://blog.youkuaiyun.com/m0_75144071/article/details/143021601?spm=1001.2014.3001.5502
这里链接给到大家,就不细讲了,感兴趣的小伙伴可以点击链接,查看一下!
整体代码如下:
// 函数功能:将数组中的零元素移动到数组末尾,保持非零元素的相对顺序不变
// 参数:
// nums:指向整数数组的指针
// numsSize:数组的大小
void moveZeroes(int* nums, int numsSize) {
// j用于记录非零元素应放置的位置
int j = 0;
// 遍历整个数组
for(int i = 0; i < numsSize; i++) {
// 如果当前元素不为零
if(nums[i]!= 0) {
// 将非零元素放置到索引为j的位置,并将j向后移动一位
nums[j++] = nums[i];
}
}
// 将剩余位置(从j开始到数组末尾)填充为零
for(; j < numsSize; j++) {
nums[j] = 0;
}
}
这个方法在时间复杂度上要比第一个好很多,这个算法可以达到O(N)
四、优化代码
题解一中的while(true),我在这里其实是非常不建议大家使用的,除非大家是物联网相关专业的同学(这里懂得都懂😁😁😁)。在实际开发过程中,如果有可以代替while(true)的方法,大家一定不要选择while(true)。
我们可以把外层循环的while(true)修改成这样:
// 外层循环从索引1开始,遍历数组
for (int j = 1; j < numsSize; j++) {
// 定义一个标志变量flag,初始值为true,表示尚未进行交换操作
bool flag = true;
// 内层循环用于检查数组中的相邻元素
for (int i = 0; i + 1 < numsSize; i++) {
// 如果当前元素为0且下一个元素不为0,则进行交换操作
if (nums[i] == 0 && nums[i + 1]!= 0) {
// 将标志变量flag设为false,表示进行了交换操作
flag = false;
// 将下一个非零元素赋值给当前位置
nums[i] = nums[i + 1];
// 将当前位置的下一个元素设为0
nums[i + 1] = 0;
}
}
// 如果在一次内层循环中没有进行交换操作(即flag为true),则跳出外层循环
if (flag)
break;
}
今天关于“Move Zeros(移动零)”算法问题的探讨就到这里啦。通过对这两个问题的深入分析,我们不仅掌握了具体的代码实现,还理解了背后的算法思路。希望这些内容能够帮助大家在算法学习的道路上更进一步,无论是应对面试中的算法题,还是提升自己的编程能力,都能有所裨益。算法的世界广阔而深邃,让我们一起保持探索的热情,不断挖掘其中的奥秘,期待下一次与大家分享更多精彩的算法知识!感谢大家的收看!!!