【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++;
        }
    }

后续也会坚持更新我的LeetCode刷题笔记,欢迎大家关注我,一起学习。
如果这篇文章对你有帮助,或者你喜欢这篇题解,可以给我点个赞哦。
若有错误,欢迎在评论区与我交流。
欢迎大家订阅我的专栏!

往期回顾:
LeetCode27.移除元素
LeetCode26.删除有序数组中的重复项

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值