算法:将数组中的0移动到末尾

36 阅读1分钟
  • 如输入[1,0,3,0,11,0] 输出 [1,3,11,0,0,0]
  • 只移动0,其他顺序不变
  • 必须在原数组进行操作

如果不限制“必须在原数组操作”

  • 定义part1 part2 两个数组
  • 遍历数组,非0push到part1, 0 push到part2
  • 返回part1.concat(part2)

传统思路(不可用)

  • 遍历数组,遇到0则push到数组末尾
  • 用splice截取当前元素
  • 时间复杂度是O(n^2)--算法不可用

代码演示(废弃方案)

function moveZero(arr: number[]): void{
  const length = arr.length
  if (length === 0) return 
  let zeroLength = 0
  // O(n^2)
  for (let i = 0; i < length-zeroLength; i++){
    if (arr[i] == 0) {
      arr.push(0)
      arr.splice(i, 1) // 复杂度O(n)
      i-- // 数组截取了一个元素,i要递减, 否则连续0 就会有错误
      zeroLength++ // 累计 0 的长度
    }
  }
}

// 功能测试

const arr3 = [1, 0, 3, 4, 0, 0, 11, 0]
moveZero(arr3)
console.log(arr3);

双指针思路(任何嵌套循环都可以考虑双指针)

  • 定义j指向第一个0,i指向j后面的第一个非0
  • 交换i和j的值,继续向后移动
  • 只遍历一次,所以时间复杂度为O(n)

image.png

代码


function moveZero2(arr: number[]): void{
    const length = arr.length
  if (length === 0) return 
  let i // 指向0后的第一个非0
  let j = -1 // 指向第一个0
  for (i = 0; i < length; i++) {
    if (arr[i] === 0) {
      // 第一个0 
      if (j < 0) {
        j = i;
      }
    }
    if (arr[i] !== 0 && j >= 0) {
      // 交换
      const n = arr[i]
      arr[i] = arr[j]
      arr[j] = n;
      j++
    }
    
  }
}
// 功能测试

const arr3 = [1, 0, 3, 4, 0, 0, 11, 0]
moveZero2(arr3)
console.log(arr3);

划重点

  • 向面试官确认:是否必须修改原数组
  • 数组是连续存储,要慎用spilce unshift等api
  • 双指针思路