「前端每日一问(61)」移动零

243 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第26天,点击查看活动详情

本题难度:⭐ ⭐ ⭐

本题类型:算法、手写

阿林最近忙爆了,真的没时间认真写文章了,但是再忙也不能忘了学习,不然就懈怠了,最近多更新点前端面试中出现的高频算法题吧,这个写起来简单。

题目描述

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

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

原题地址:leetcode-283. 移动零

题目要求:

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

解题思路

在题目中看到了 原地 这样的字眼,直接使用双指针,这道题用快慢指针。

思路是:

  • 定义慢指针 i,快指针 j
  • 两个指针一起往后走,遇到指向为 0 的情况,慢指针就停下来,快指针则不管,一直往后走
  • 如果 nums[j] !== 0 && nums[i] === 0 ,快慢指针位置的元素交换位置
  • 快指针走完,循环结束。

先用文字“画图”,理清思路:

   初始化:
   
   i    
  [1,0,1,0,3,12]
   j
  
  下一步:
  
     i    
  [1,0,1,0,3,12]
     j
  
  下一步:
  
     i    
  [1,0,1,0,3,12]
       j
  
  这里要注意
  此时,nums[j] !== 0 && nums[i] === 0
  交换位置,然后 i++,j++
  下一步:
  
       i    
  [1,1,0,0,3,12]
         j
  
  下一步:
  
       i     
  [1,1,0,0,3,12]
           j
  
  下一步:
  
         i     
  [1,1,3,0,0,12]
              j
  
   下一步:
  
            i     
  [1,1,3,12,0,0]
                j
   
   循环结束。

编码实现

写代码的时候还是要语意化,定义快指针为 fast,慢指针为 slow

// 快慢指针
function moveZeroes(nums) {
  let slow = 0, fast = 0
  while(fast < nums.length) {
    // 快指针指向不为0,慢指针指向为0,交换
    if (nums[fast] !== 0 && nums[slow] === 0) {
      [nums[fast],nums[slow]] = [nums[slow],nums[fast]]
      slow++
    }
    // 慢指针指向不为0,才往后走,否则慢指针不动
    if (nums[slow] !== 0) {
      slow++
    }
    // 快指针一直往后面走
    fast++
  }
  return nums
}

这里有一个细节,交换快慢指针对应的元素是这么写的:

[nums[fast],nums[slow]] = [nums[slow],nums[fast]]

一般我们交换数组里的两个元素都是这么写的:

  • 定义一个临时变量 temp
  • temp = arr[i]
  • arr[i] = arr[j]
  • arr[j] = temp

在 JS 中,可以使用 解构赋值 语法来实现数组元素的交换,代码更简洁。

// swap1 和 swap2 效果完全一样
function swap1(arr, i, j) {
    let temp = arr[i]
    arr[i] = arr[j]
    arr[j] = temp
    return arr
}

function swap2(arr, i, j) {
    [arr[i], arr[j]] = [arr[j], arr[i]]
    return arr
}

console.log(swap1([1,2], 0, 1)) // 输出 [2,1]
console.log(swap2([1,2], 0, 1)) // 输出 [2,1]

回到本题,细心地同学还可以发现,上面的逻辑其实有冗余。

  • 慢指针的指向是 0 时,交换后会往后走。
  • 慢指针的指向不是 0 时,也会往后走。

arr8.png

可以合并一下代码,合并后代码如下:

function moveZeroes(nums) {
  let slow = 0, fast = 0
  while(fast < nums.length) {
    if (nums[fast] !== 0) {
      [nums[fast],nums[slow]] = [nums[slow],nums[fast]]
      slow++
    }
    fast++
  }
  return nums
}

这样看上去代码就简洁多了。

写代码就是这样,除非你经验非常丰富,否则很难一步就写出优质的代码。我们要做的是先理清思路,把功能实现,再去慢慢优化代码。

一步一个脚印,多去训练,慢慢地你的代码就会越写越好。

运行结果

vscode leetcode 插件 yyds! 上班“认真工作”的神器 🐶

image.png 结尾

阿林水平有限,文中如果有错误或表达不当的地方,非常欢迎在评论区指出,感谢~

如果我的文章对你有帮助,你的👍就是对我的最大支持^_^

我是阿林,输出洞见技术,再会!

上一篇:

「前端每日一问(60)」删除有序数组中的重复项

下一篇:

「前端每日一问(62)」如何只截取一个数值的整数部分