LeetCode题练习与总结:移动零--283

299 篇文章 0 订阅
81 篇文章 0 订阅

一、题目描述

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

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

示例 1:

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

示例 2:

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

提示:

  • 1 <= nums.length <= 10^4
  • -2^31 <= nums[i] <= 2^31 - 1

二、解题思路

这个问题可以通过双指针的方法来解决。我们可以定义两个指针,一个指针(我们可以称之为lastNonZeroFoundAt)指向最后一个非零元素的索引,另一个指针(我们可以称之为cur)用于遍历数组。

遍历数组时,如果当前元素不为零,则将其与lastNonZeroFoundAt指向的元素交换,并将lastNonZeroFoundAt指针向前移动一位。这样可以保证所有非零元素都被移动到了数组的前面,且相对顺序不变。遍历结束后,所有在lastNonZeroFoundAt之后的位置都应该设置为0。

以下是具体的步骤:

  1. 初始化lastNonZeroFoundAt为0。
  2. 遍历数组,使用cur指针。
  3. 如果nums[cur]不为0,则交换nums[cur]nums[lastNonZeroFoundAt],并将lastNonZeroFoundAt向前移动一位。
  4. 继续遍历,直到cur到达数组末尾。
  5. lastNonZeroFoundAt到数组末尾的所有位置设置为0。

三、具体代码

class Solution {
    public void moveZeroes(int[] nums) {
        if (nums == null || nums.length == 0) return;

        int lastNonZeroFoundAt = 0;
        // 将所有非零元素移动到数组前面
        for (int cur = 0; cur < nums.length; cur++) {
            if (nums[cur] != 0) {
                // 交换当前非零元素与lastNonZeroFoundAt指向的元素
                int temp = nums[cur];
                nums[cur] = nums[lastNonZeroFoundAt];
                nums[lastNonZeroFoundAt] = temp;
                // 更新lastNonZeroFoundAt
                lastNonZeroFoundAt++;
            }
        }
        
        // 将lastNonZeroFoundAt之后的所有元素设置为0
        for (int i = lastNonZeroFoundAt; i < nums.length; i++) {
            nums[i] = 0;
        }
    }
}

这段代码实现了将所有0移动到数组末尾,并且保持了非零元素的相对顺序,同时没有使用额外的数组空间,满足了原地对数组进行操作的要求。

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 遍历数组:代码中有一个for循环,用于遍历数组中的每个元素。这个循环会执行nums.length次,因此这部分的时间复杂度是O(n),其中n是数组的长度。

  • 交换操作:在遍历数组的过程中,每当遇到一个非零元素,就会执行一次交换操作。在最坏的情况下(即数组中所有元素都是非零的),这个操作也会执行nums.length次。交换操作是常数时间的,因此这部分的时间复杂度同样是O(n)。

  • 设置零操作:在遍历数组之后,有一个for循环用于将剩余的元素设置为0。在最坏的情况下(即数组中没有任何零),这个循环也会执行nums.length次。每次设置操作是常数时间的,因此这部分的时间复杂度也是O(n)。

由于以上三部分操作是顺序执行的,因此总的时间复杂度是这三个操作的累加,即O(n) + O(n) + O(n) = O(n)。

2. 空间复杂度
  • 临时变量:代码中使用了一个临时变量temp用于交换操作,这个变量占用了常数空间,因此空间复杂度是O(1)。

  • 辅助空间:除了输入数组之外,代码没有使用任何额外的数据结构(如数组、列表等)来存储数据,因此没有使用额外的空间。

综上所述,总的空间复杂度是O(1),即代码使用了常数额外空间。

五、总结知识点

  • 数组的操作

    • 数组元素的访问:通过索引直接访问数组元素。
    • 数组元素的赋值:直接给数组元素赋值。
  • 条件判断

    • if语句:用于判断数组元素是否为零。
    • nulllength属性检查:用于判断数组是否为空或长度为零。
  • 循环结构

    • for循环:用于遍历数组中的所有元素。
  • 指针/索引概念

    • 使用变量lastNonZeroFoundAt作为指针,指向数组中最后一个非零元素的索引位置。
    • 使用变量cur作为当前遍历的索引。
  • 交换操作

    • 使用临时变量temp来交换两个数组元素的值。
  • 算法思想

    • 双指针(或双索引)技术:通过两个指针来维护数组中非零元素的位置。
  • 原地算法

    • 原地修改数组,不使用额外的存储空间来保存结果。
  • 边界条件处理

    • 在函数开始时检查输入数组的边界条件,即是否为null或长度为零。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一直学习永不止步

谢谢您的鼓励,我会再接再厉的!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值