小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
JS 中是没有指针(pointer)的概念的,只有引用(reference),一个简单的判断依据就是:C语言中指针是可以有 ++ 操作的,但 JS 办不到。JS 引用与指针类似的地方在于他可以对同一个地址的值进行操作,我们可以简单地认为 JS 变量储存了一个对象的地址。本文以双指针(快慢指针)概念分析解题思路,JS代码为题解
leetcode283题(移动零)
题目描述
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。尽量减少操作次数。
思路及代码实现
方案1
- 在不考虑说明的要求下,可以借用新数组的方法。先遍历原数组,判断元素如果不为0,则依次把值赋值到新的数组中,一次遍历后,把新数组剩余长度元素都赋值为0
- 此时时间复杂度和空间复杂度都为
O(n)let moveZeroes = (nums) => { let newArr = [] for(let i = 0;i < nums.length; i++) { if (nums[i] !== 0) { newArr.push(nums[i]) } } let length = newArr.length - 1 for(let i = length; index < nums.length - length; i++) { newArr[i] = 0 } return newArr }
方案2
-
在符合说明的要求下,使用双指针(快慢指针)解法:快针不断向右移动,每次快指针指向非零数,则将快慢指针对应的数交换,同时慢指针右移。最后慢针左边均为非零数,快针左边直到慢指针处均为零。因此每次交换,都是将慢指针的零与快指针的非零数交换,且非零数的相对顺序并未改变。
-
此时时间复杂度为
O(n),空间复杂度为O(1)// 每一次遍历会执行 fast++,而慢指针只有在元素不为0时才执行 slow++,因此形成快慢指针 var moveZeroes = function(nums) { let slow = 0 // 定义慢指针 for(let fast = 0;fast < nums.length; fast++) { if(nums[fast] !== 0) { if (fast !== slow) { // 快慢指针如果在同一位置,没有必要操作交换(减少操作次数) let temp = nums[slow] nums[slow] = nums[fast] nums[fast] = temp } slow++ } } }
方案3
-
方案2在交换的过程中,
slow或者fast所在元素都被访问2次;如果从尽量减少访问的次数角度看,可以换成直接赋值的思路代替原来交换的方式。最后将slow到fast之间的元素赋值为0 -
此时时间复杂度为
O(n),空间复杂度为O(1)var moveZeroes = function(nums) { let slow = 0 for(let fast = 0;fast < nums.length; fast++) { if(nums[fast] !== 0) { if (fast !== slow) { nums[slow] = nums[fast] } slow++ } } // 将 slow 到 fast 之间的元素赋值为0 for(let i = slow; slow < nums.length; slow++) { nums[slow] = 0 } }
总结
- 方案2、3差异不大,只是从不同角度考虑解决思路
- 在解决基于数组的算法中,通常一个指针用于数组遍历,另一个指针用于记录信息。这种双指针(快慢指针)的思路在某些场景下可以降低空间复杂度,提高性能