• 11、将数组中的 0 移动到末尾


    将数组中的0移动到数组末尾

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

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

    • 定义arr1、arr2 两个数组
    • 遍历数组,非0的push到arr1, 0的push到arr2
    • 最后返回arr1.concat(arr2)

    传统思路

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

    代码实现

    function moveZero1 (arr:number[] ):void {
      const len = arr.length
      if(len === 0) return
    
      // 记录0的个数
      let zeroLen = 0
    
      //O(n^2)
      for (let i = 0; i < len - zeroLen; i++) {
        if (arr[i] === 0) {
          arr.push(0) // 在末尾加0
          arr.splice(i,1) // 删除原来的0,O(n)
          i-- // 数组截取了一个元素,i要递减,否则遇到连续的0户报错
          zeroLen++ // 记录0的个数 +1
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    功能测试

    const arr = [1,0,3,0,11,0]
    moveZero1(arr)
    console.log(arr)
    
    • 1
    • 2
    • 3

    结果

    [ 1, 3, 11, 0, 0, 0 ]
    
    • 1

    单元测试

    describe('移动数组的0到末尾',() => {
      it('正常情况', () => {
        const arr = [1,0,3,0,11,0]
        moveZero1(arr)
        expect(arr).toEqual([ 1, 3, 11, 0, 0, 0 ])
    
      })
    
      it('没有0', () => {
        const arr = [ 1, 3, 11]
        moveZero1(arr)
        expect(arr).toEqual([ 1, 3, 11])
      })
    
      it('全是0', () => {
        const arr = [ 0,0,0,0,0]
        moveZero1(arr)
        expect(arr).toEqual([ 0,0,0,0,0])
      })
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    使用双指针优化

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

    代码实现

    function moveZero2 (arr:number[]):void {
      const len = arr.length
      if(len === 0) return
    
      let i 
      let j = -1 // 指向第一个0
    
      for (i = 0; i < len; i++) {
        if (arr[i] === 0) {
          if (j < 0) {
            j = i
          }
        }
    
        if (arr[i] !== 0 && j >= 0) {
          // 交换
          const temp = arr[i] // 非0 赋值给临时变量
          arr[i] = arr[j] // 将0移动到原来非0的位置
          arr[j] = temp // 非0 赋值给原来0的位置
          j++ // 交换完毕,向前一步
    
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    功能测试

    const arr = [ 1, 11, 0, 0, 3, 0 ]
    moveZero2(arr)
    console.log(arr)
    
    • 1
    • 2
    • 3

    结果

    [ 1, 11, 3, 0, 0, 0 ]
    
    • 1

    单元测试

    describe('移动数组的0到末尾',() => {
      it('正常情况', () => {
        const arr = [1,0,3,0,11,0]
        moveZero2(arr)
        expect(arr).toEqual([ 1, 3, 11, 0, 0, 0 ])
    
      })
    
      it('没有0', () => {
        const arr = [ 1, 3, 11]
        moveZero2(arr)
        expect(arr).toEqual([ 1, 3, 11])
      })
    
      it('全是0', () => {
        const arr = [ 0,0,0,0,0]
        moveZero2(arr)
        expect(arr).toEqual([ 0,0,0,0,0])
      })
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    性能比较

    const test1 = []
    for (let i = 0; i < 100 * 10000; i++) {
      if(i % 10 === 0) {
        test1.push(0)
      } else {
        test1.push(i)
      }
      
    }
    console.time('moveZero1')
    moveZero1(test1)
    console.timeEnd('moveZero1')
    
    const test2 = []
    for (let i = 0; i < 100 * 10000; i++) {
      if(i % 10 === 0) {
        test2.push(0)
      } else {
        test2.push(i)
      }
      
    }
    
    console.time('moveZero2')
    moveZero2(test2)
    console.timeEnd('moveZero2')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    打印结果

    moveZero1: 29.445s
    moveZero2: 3.641ms
    
    • 1
    • 2

    100万数据测试,双指针完胜循环嵌套

    总结

    • 向面试官确认:是否必须修改原数组?
    • 数组是连续存储结构,要慎用splice、unshift等API
    • 双指针思路
  • 相关阅读:
    多视图聚类的论文阅读(一)
    Image does NOT change color when selecting it in tiptap
    现有TiDB集群扩展pump/drainer作为binlog文件落地
    C. Monsters And Spells
    【Rust日报】2022-08-31 RustDesk 跻身 Rust 开源项目 Top 10 第九名
    为什么全局钩子必须写到dll里面?
    元老职员离职申请书怎么写模板,共计10篇
    2022哪个牌子的台灯质量好?双十一值得入手的好用护眼台灯推荐
    php的短信验证的流程,如何实现前端js加后端php
    基于.Net 的 AvaloniUI 多媒体播放器方案汇总
  • 原文地址:https://blog.csdn.net/qq_36362721/article/details/127680756