移动零的四种实现方式(逐步优化)

10 篇文章 0 订阅
9 篇文章 0 订阅

今天刷的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指向第一个未处理0fast指向第一个未处理非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)的时间复杂度

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值