【LeetCode283.移动零】——双指针法_双指针

283.移动零

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

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

示例1

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

示例2:

输入: nums = [0]
输出: [0]

提示:

  • ​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++;
}
}