【LeetCode283.移动零】——双指针法
原创
©著作权归作者所有:来自51CTO博客作者mb1d7843c586h5f的原创作品,请联系作者获取转载授权,否则将追究法律责任
283.移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例2:
提示:
-
1 <= nums.length <= 104
-
-231 <= nums[i] <= 231 - 1
思路:
本题是经典的双指针算法题,对于一个数组来说,删除一个元素是比较复杂的,每次删除元素后,若要保留原有的顺序,则需要将后面的元素一个个先前“挪”一格位置,这样操作的话,算法的时间复杂度是较高的。
所以就有了双指针法,分为快慢指针法以及左右指针法,能够实现在一个for循环下完成两个for循环的工作。
两遍循环:
我们可以利用一种最朴素的方法,即暴力地使用两遍for循环进行求解,外层的for循环用于查找所要移除的元素,当寻找到题目中所要移除的0后,利用内层的for循环,将数组集体向前移动一位。
在结束内层的for循环后,再将0赋值给数组的末尾。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int size = nums.size();
for (int i = 0; i < size; i++) {
if (nums[i] == 0) { // 发现0
cout << i << endl;
for (int j = i + 1; j < nums.size(); j++) { //将数组集体向前移动一位
nums[j - 1] = nums[j];
}
nums[nums.size() - 1] = 0;//给数列尾部赋0
i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
size--;//记录非零数组大小
}
}
}
};
该方法的时间复杂度较高,LeetCode勉强通过。
快慢双指针:
设置快慢双指针,分别指向数组,完成各自任务。
快指针:不停止,勇往直前,寻找0
慢指针:用于记录更新新数组的下标位置\
在寻找完成后,要给尾部剩下的元素赋值为0,从而达到题目要求。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int slow = 0; //初始化慢指针
for (int fast = 0; fast < nums.size(); fast++) { //快指针对整个数组进行遍历,寻找0
if (0 != nums[fast]) { //若不为0,更新慢指针,并赋值给新数组
nums[slow] = nums[fast];
slow++;
}
}
//给数列尾部赋0
while(slow<nums.size())
{
nums[slow] = 0;
slow++;
}
}
};
利用该方法大大降低了时间复杂度。
优化版本:
可以在快指针移动的过程中,就给当前快指针所指向的元素赋值为0,不用后续再进行赋0的操作。
class Solution {
public:
//快慢指针优化版,不必在最后进行尾部赋0操作
void moveZeroes(vector<int>& nums) {
int fast = 0;
int slow = 0;
while (fast < nums.size()) {
if (nums[fast] != 0) {
// 防止 fast = slow 的情况下把 nums[fast]或者nums[slow]赋值为0
int temp = nums[fast];
nums[fast] = 0;
nums[slow] = temp;
fast++;
slow++;
}
else {
fast++;
}
}
}
};
左右双指针:
左右双指针的方法在本题并不适用,原因就在于题目要求要保持非零元素的相对顺序,左右双指针方法的运用则会打乱原本的顺序,只能够做到删除所有的零元素。
//左右指针法,会改变原本的相对顺序,不符合题意
void moveZeroes(vector<int>& nums) {
int left = 0; //初始化左指针
int right = nums.size() - 1; //初始化右指针
while (left <= right) {
// 找左边等于0的元素
while (left <= right && nums[left] != 0) { //若不等于0,继续遍历
++left;
} //找到等于0的元素时,结束循环
// 找右边不等于0的元素
while (left <= right && nums[right] == 0) {
--right;
} //找到不等于0的元素时,结束循环
// 将右边不等于0的元素覆盖左边等于0的元素
if (left < right) {
nums[left++] = nums[right--];
}
}
//给数列尾部赋0
while (left < nums.size())
{
nums[left] = 0;
left++;
}
}