今天刷的leetcode题,看似简单,但想要优化却不简单,这里记录一下我的解题过程
题目:
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/move-zeroes
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析:
题目很简单,就是把全部的0移动到后面并且不用额外空间,下面直接看解法
解法一:
1.暴力破解法:
直接把0一个个循环交换到最后,代码如下:
void moveZeroes(vector<int>& nums) {
int index = nums.size() - 1, right = right;
// 获取倒数第一个非0下标
while(index >= 0 && nums[index] == 0) index--;
if(index <= 0)
return;
right = index;
for(; index >= 0; index--){
if(nums[index] == 0){
//用覆盖的方式移动0
for(int i = index + 1; i <= right; i++){
nums[i - 1] = nums[i];
}
nums[right] = 0;
while(index >= 0 && nums[index] == 0) index--;
right--;
}
}
}
虽然简单粗暴,但是耗时太多, 时间复杂度在 O(n^2)
2.删除元素法:
把0删除后,在数组后增加0,代码如下:
void moveZeroes(vector<int>& nums) {
int index = nums.size() - 1, right = right;
// 获取倒数第一个非0下标
while(index >= 0 && nums[index] == 0) index--;
if(index <= 0)
return;
int zeroC = 0;
for(; index >= 0; index--){
//找到0后,把0删除
if(nums[index] == 0){
nums.erase(nums.begin() + index);
zeroC++;
}
}
for(int i = 0; i < zeroC; i++){
nums.emplace_back(0);
}
}
该方法看着比第一种好,时间复杂度表面上看是 O(N),但是删除元素时需要不断移动元素,实际消耗比O(N)大,比O(N^2)小
3.压缩数据法
该法是第二种方法的变化,不删除0,而是把非0元素 覆盖 0元素,代码如下:
void moveZeroes(vector<int> nums) {
if (nums.size() == 0)
return;
int index = 0;
//一次遍历,把非零的都往前挪
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != 0)
nums[index++] = nums[i];
}
//后面的都是0,
while (index < nums.size()) {
nums[index++] = 0;
}
}
该法不需要删除数据,只需要两次变量就可以得到结果,方法应该比第二种快1倍左右。
4.双指针法
这种方法一开始想不到,但是确是一种非常有效且简洁的算法;
代码如下:
void moveZeroes(vector<int>& nums) {
for(int low = 0, fast = 0; fast < nums.size(); fast++){
if(nums[fast] != 0){
// 把 0交换到非0后面
swap(nums[low++], nums[fast]);
}
}
}
思路:循环遍历直到出现low指向第一个未处理0,fast指向第一个未处理非0;
然后交换数据,同时low++,这样 0元素就会跑到 非 0元素后面;
步骤如下:
arr = {0,1,0,2}; 一开始 low = 0, fast = 0;
fast = 0时, nums[fast] == 0, 跳过
fast = 1时, nums[fast] != 0,执行交换后得:
arr = {1,0,0,2}; low = 1, fast = 1;
fast = 2 时, nums[fast] == 0, 跳过
fast = 3时, nums[fast] != 0, 此时low 还是 == 1,执行交换后得:
arr = {1,2,0,0}; low = 2, fast = 3;
循环结束,最后得到结果, 1,2,0,0
该方法非常优秀,只用O(N)的时间复杂度