0基础leetcode练习(移动零)

一、题目介绍

这是移动零的网址,大家可以看完博客的思路去练习一下。

给定一个数组 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

普通方法:

其实这个很简单,把非零都移到前面去之后,后面剩下的直接补0就好。

void moveZeroes(int* nums, int numsSize)
{
        int count = 0;
        for (int i = 0; i < numsSize; i++) 
        {
            if (nums[i] == 0)
            {
                count++;
                continue;
            }
            nums[i - count] = nums[i];
        }
       while (count-- > 0) {
    nums[numsSize - count - 1] = 0;
}
}

实现思路:

1. 初始化计数器:定义一个变量 `count` 来记录数组中零的个数。

2. 遍历数组:使用一个 `for` 循环遍历数组 `nums`。

3. 处理非零元素:在循环中,如果当前元素 `nums[i]` 不是零,就将其复制到索引 `i - count` 的位置。由于 `count` 记录了前面有多少个零,所以 `i - count` 就是当前非零元素应该在的位置。

4. 更新计数器:如果当前元素是零,`count` 就递增,表示遇到了一个零值,但是不进行元素交换,因为零值将在后面统一处理。

5. 填充零值:在 `for` 循环结束后,使用一个 `while` 循环从数组的末尾向前填充零。由于前面已经将所有非零元素移动到了数组的前部,所以数组的末尾 `count` 个位置需要填充零。

6. 更新数组:在 `while` 循环中,每次循环将数组的倒数第 `count` 个位置设置为零,并且 `count` 递减。这样,每次循环都将零放在正确的位置,直到 `count` 变为零。

7. 循环结束:当 `count` 变为零时,所有的零都被移动到了数组的末尾,且非零元素的相对顺序没有改变。

这段代码的关键在于使用一个计数器 `count` 来跟踪零的数量,并在遍历过程中将非零元素移动到数组的前部。然后,使用另一个循环将剩余的位置填充为零。这种方法的时间复杂度是 O(n),其中 n 是数组 `nums` 的长度,因为我们只遍历了数组两次(一次移动非零元素,一次填充零)。空间复杂度是 O(1),因为我们只使用了有限的额外空间(即 `count` 变量)。

双指针方法:

void swap(int *a, int *b) {
    int t = *a;
    *a = *b, *b = t;
}

void moveZeroes(int *nums, int numsSize) {
    int left = 0, right = 0;
    while (right < numsSize) {
        if (nums[right]) {
            swap(nums + left, nums + right);
            left++;
        }
        right++;
    }
}

思路及解法

使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。

右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。

注意到以下性质:

左指针左边均为非零数;

右指针左边直到左指针处均为零。

因此每次交换,都是将左指针的零与右指针的非零数交换,且非零数的相对顺序并未改变。

交换法:

void moveZeroes(int* nums, int numsSize) {
    // `j` 指向数组中下一个非零元素应该放置的位置
    int j = 0;

    // 遍历数组
    for (int i = 0; i < numsSize; i++) {
        // 如果当前元素不是零,则将其移动到 `j` 的位置
        if (nums[i] != 0) {
            nums[j++] = nums[i]; // 将非零元素复制到 `j` 的位置,并递增 `j`
        }
    }

    // 此时 `j` 指向数组中第一个零的位置
    // 从 `j` 开始填充零,直到数组末尾
    for (; j < numsSize; j++) {
        nums[j] = 0;
    }
}

实现思路:

  1. 初始化指针:定义一个指针 j,它用来指向数组中下一个非零元素应该放置的位置。

  2. 遍历数组:使用一个 for 循环遍历数组 nums

  3. 处理非零元素:在循环中,如果当前元素 nums[i] 不是零,就将其复制到索引 j 的位置。然后递增 j,准备放置下一个非零元素。

  4. 省略条件判断:由于 j 总是指向下一个非零元素应该放置的位置,所以不需要检查 ij 是否相等,直接进行赋值和 j 的递增。

  5. 填充零值:在 for 循环结束后,j 将指向数组中第一个零应该放置的位置。此时,使用另一个 for 循环从 j 开始,将数组的剩余位置填充为零。

  6. 循环结束:第二个 for 循环继续执行,直到 j 达到数组末尾 numsSize,确保所有剩余的位置都被设置为零。

  7. 原地操作:整个操作都在原数组 nums 上进行,没有使用额外的数组空间,因此是原地(in-place)操作。

  8. 时间复杂度:这个算法的时间复杂度是 O(n),其中 n 是数组 nums 的长度,因为我们只遍历了数组两次(一次移动非零元素,一次填充零)。

  9. 空间复杂度:空间复杂度是 O(1),因为我们只使用了有限的额外空间(即 j 变量)。

 本期内容向大家介绍了这道OJ题的三种做法,其实这些思想可以贯穿到很多题中,大家平时要多加练习,才能写出更好更妙的代码。加油各位!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值