Advanced-Frontend/Daily-Interview-Question

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

yygmind opened this issue · 252 comments

示例:

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

说明:

  1. 必须在原数组上操作,不能拷贝额外的数组。

  2. 尽量减少操作次数。

新增:解决了有连续的0无法实现功能的问题。

function zeroMove(array) {
        let len = array.length;
        let j = 0;
        for(let i=0;i<len-j;i++){
            if(array[i]===0){
                array.push(0);
                array.splice(i,1);
                i --;
                j ++;
            }
        }
        return array;
    }
        const moveZore = (arr) => {
            let n = 0
            arr.forEach((item, index) => {
                if (item === 0){
                    arr.splice(index, 1)
                    n++;
                }
            })
            arr.push(...(new Array(n)).fill(0))
            return arr;
        }
y1324 commented

let nums = [0,1,0,3,12];

for (let i = 0; i < nums.length; i++){
  if (nums[i] === 0){
    nums.push(0);
    nums.splice(i,1);
  }
};

let arr = [1,2,3,0,7,0]
j = arr.length
for (i = 0; i < j; i++) {
if (arr[i] === 0) {
arr.splice(i,1)
arr.push(0)
}
}

btea commented
function move(arr){ 
  var n = 0; 
  for(var i = 0; i < arr.length; i++){ 
    if(arr[i] === 0){ 
       arr.splice(i, 1);
       n++; 
       i--; 
    }
  } 
  while(n--){ 
    arr.push(0); 
  }
  return arr;
}

已修正连续多个 0 时的 bug
思路:用一个 len 记录 0 的出现次数,再过滤掉原数组中所有的 0,最后在数组末尾补上被移除的 0

let nums = [0, 0, 0, 1, 0, 3, 12];

console.log(moveZeroToLast(nums));  // [1, 3, 12, 0, 0, 0, 0]

function moveZeroToLast(arr) {
  let len = arr.length;

  return arr.filter(it => it === 0 ? len-- && false : true)
    .concat(Array(arr.length - len).fill(0));
}

上面所有的再循环中,先 splice 在 push 的方法都是有问题的

因为,当splice 一个元素的时候,紧跟着的后面一个元素会向前移动一位,索引会变成正在删除那个元素的,所有当有连续的 0 时候,无法满足要求

const array = [2, 0, 1, 4, 0, 0, 0, 5, 7, 8];
array.sort((fir, sec) => {
  return fir === 0;
})
// array = [ 2, 1, 4, 5, 7, 8, 0, 0, 0, 0 ]

这里有个问题,首元素为0无效,不知道为什么,求教

const array = [0, 2, 0, 1, 4, 0, 0, 0, 5, 7, 8];
array.sort((fir, sec) => {
  return fir === 0;
})
console.log(array);
// array = [ 8, 7, 1, 4, 2, 5, 0, 0, 0, 0, 0 ]

let moveZero = (arr) => {
let point = 0
for (var i = 0; i < arr.length; i++) {
if (arr[i]!=0) {
arr[point] = arr[i]
arr[i] = 0
point++
}
}

return arr
}

最优解法

想到好几个方法,楼上都写出来了,但是我觉得这些都不是最优解法,既然是算法题,自然是以算法的思维去解。

function moveZeroToLast(arr) {
    let index = 0;
    for (let i = 0, length = arr.length; i < length; i++) {
        if (arr[i] === 0) {
            index++;
        } else if (index !== 0) {
            arr[i - index] = arr[i];
            arr[i] = 0;
        }
    }
    return arr;
}
y1324 commented

上面所有的再循环中,先splice在推的方法都是有问题的

因为,当splice一个元素的时候,紧跟着的后面一个元素会向前移动一位,索引会变成正在删除那个元素的,所有当有连续的​​0时候,无法满足要求

那个这个问题怎么解决?没头绪

const moveZeroToEnd = (arr) => {
    let index = 0
    let current = 0
    while(index < arr.length) {
        if (arr[current] === 0) {
            arr.splice(current, 1)
            arr.push(0)
        } else {
            current++
        }
        index++
    }
    return arr
}
//倒序循环可避免
const arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 1, 9, 9, 9, 0, 0, 0, 0, 1, 0, 3, 12, 0, 0, 0, 0];
    const len = arr.length;
    console.log(len)
    for (let i = len; i >= 0; i--) {
      if (arr[i] === 0) {
        arr.splice(i, 1);
        arr.push(0)
      }
    }
    console.log(arr)
y1324 commented
//倒序循环可避免
const arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 1, 9, 9, 9, 0, 0, 0, 0, 1, 0, 3, 12, 0, 0, 0, 0];
    const len = arr.length;
    console.log(len)
    for (let i = len; i >= 0; i--) {
      if (arr[i] === 0) {
        arr.splice(i, 1);
        arr.push(0)
      }
    }
    console.log(arr)

为什么倒序可以解决这个办法

//倒序循环可避免
const arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 1, 9, 9, 9, 0, 0, 0, 0, 1, 0, 3, 12, 0, 0, 0, 0];
    const len = arr.length;
    console.log(len)
    for (let i = len; i >= 0; i--) {
      if (arr[i] === 0) {
        arr.splice(i, 1);
        arr.push(0)
      }
    }
    console.log(arr)

为什么倒序可以解决这个办法

因为待处理的数据索引没有变。https://www.jianshu.com/p/77247e9e1849

  • 首先,题意是要在原地修改数组,那么sort,concat之类的纯函数方法就是行不通的了,因为是返回新的数组,而不是在原地修改
  • 其次,splice的时间复杂度是O(n),那么使用splice的算法的时间复杂度是O(n2),既然在写算法,那么就要寻求时间复杂度与空间复杂度最低的办法。

思路:双指针

  • 设定一个慢指针一个快指针,快指针每次+1, 当慢指针的值不等于0的时候也往后移动,当慢指针等于0并且快指针不等于0的时候,交换快慢指针的值,慢指针再+1
function moveZero(arr) {
  let i = 0
  let j = 0
  while (j < arr.length) {
    if (arr[i] !== 0) {
      i++
    } else if (arr[j] !== 0) {
      ;[arr[i], arr[j]] = [arr[j], arr[i]]
      i++
    }
    j++
  }
}

时间复杂度O(n),n是数组长度,空间复杂度O(1)

//最后有啥办法不循环么,直接截取放最后
var arr = [0,1,0,3,12,0,89,0,8,0] ;

function ss (arr){
    arr.sort((a,b)=>{
        return a-b
    });
    let len = arr.lastIndexOf(0) +1;
    arr.splice(0,len);
    for (let i=0;i<len;i++){
        arr.push(0)
    }
    console.log(arr)
}
ss(arr)
// 正序循环方案
// 由于splice会将nums.length - 1,所以让数组下标自减,强制回溯到上一个已操作的元素
const moveZeroToEnd = (nums) => {
	let length = nums.length
	for (let i = 0; i < nums.length; i++) {
		let el = nums[i]
		if (el === 0) {
			nums.splice(i, 1)
			i--
		}
	}
	let tmpAry = new Array((length - nums.length)).fill(0)
	return [...nums, ...tmpAry]
}
moveZeroToEnd([1,2,0,0,0,0,0,0,3,0,4])

思路是把0剔除出来,然后再塞进去- -

function moveZero(arr) {
      let num = 0
      let index = -1
      while ((index = arr.indexOf(0)) > -1) {
        num++
        arr.splice(index, 1)
      }

      while (num--) {
        arr.push(0)
      }
}

let or = [0, 1, 0, 3, 12]
let len = or.length
while (len) {
if(!or[len-1]){
or.push(...or.splice(len-1,1))
}
len --
}
console.log(or) // [1, 3, 12, 0, 0]

function move (arr) {
let counter = 0
for (let index = 0; index < arr.length; index++) {
const item = arr[index];
if (item === 0) {
arr.splice(index, 1)
counter++
index--
}
}
arr.push(...new Array(counter).fill(0))
return arr
}

function sortArr(arr) {
    let arrLength = arr.length;
    for (let i = 0; i < arrLength; i++) {
      if (arr[i] === 0) {
        arr.splice(i, 1);
        i--;
        arrLength--;
        arr.push(0);
      }
    }
    return arr;
  }
 function moveZero(arr) {
        if (!arr instanceof Array || arr.length <= 0) return arr
        let zeroIndex = -1;
        let i = 0;
        while (i < arr.length) {
            if (arr[i] !== 0 && zeroIndex >= 0) {
                swap(i, zeroIndex, arr)
                zeroIndex++
            } else if (arr[i] === 0 && zeroIndex < 0) {
                zeroIndex = i
            }
            i++
        }
        return arr
    }

    function swap(i, j, arr) {
        [arr[i], arr[j]] = [arr[j], arr[i]]
    }
    const arr = [0, 1, 0, 3, 12]
    console.log(moveZero(arr))

原地交换,没有用多余的数组。应该是O(n)。
木有上面大佬的简洁,不知道能不能覆盖所有情况,求指正

const array = [2, 0, 1, 4, 0, 0, 0, 5, 7, 8];
array.sort((fir, sec) => {
  return fir === 0;
})
// array = [ 2, 1, 4, 5, 7, 8, 0, 0, 0, 0 ]

这里有个问题,首元素为0无效,不知道为什么,求教

const array = [0, 2, 0, 1, 4, 0, 0, 0, 5, 7, 8];
array.sort((fir, sec) => {
  return fir === 0;
})
console.log(array);
// array = [ 8, 7, 1, 4, 2, 5, 0, 0, 0, 0, 0 ]

这个我在 chrome 上试的时候无法排序,在 safari 上倒是可以按照逻辑输出 @wz71014q

上面所有的再循环中,先splice在推的方法都是有问题的
因为,当splice一个元素的时候,紧跟着的后面一个元素会向前移动一位,索引会变成正在删除那个元素的,所有当有连续的​​0时候,无法满足要求

那个这个问题怎么解决?没头绪

还用上面的方法的话,如果元素为0,可以把正在循环的索引减 1

const array = [2, 0, 1, 4, 0, 0, 0, 5, 7, 8];
array.sort((fir, sec) => {
  return fir === 0;
})
// array = [ 2, 1, 4, 5, 7, 8, 0, 0, 0, 0 ]

这里有个问题,首元素为0无效,不知道为什么,求教

const array = [0, 2, 0, 1, 4, 0, 0, 0, 5, 7, 8];
array.sort((fir, sec) => {
  return fir === 0;
})
console.log(array);
// array = [ 8, 7, 1, 4, 2, 5, 0, 0, 0, 0, 0 ]

这个我在 chrome 上试的时候无法排序,在 safari 上倒是可以按照逻辑输出 @wz71014q

const array = [0, 2, 0, 1, 4, 0, 0, 0, 5, 7, 8];
array.sort((fir, sec) => {
return (fir === 0) ? 1 : -1
})

这样试试?

如果想按照其他标准进行排序,就需要提供比较函数,该函数要比较两个值,然后返回一个用于说明>这两个值的相对顺序的数字。比较函数应该具有两个参数 a 和 b,其返回值如下:

若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。
若 a 等于 b,则返回 0。
若 a 大于 b,则返回一个大于 0 的值。

可以了,谢啦 @lo1412

HduSy commented

clock in

思路:

  1. 先把数组中的0全部去掉
  2. 对比初始数组和去掉0后的数组的长度
  3. 长度相差多少就在数组末尾添加几个0
const result = (arr) => arr.filter(Boolean).concat([...Array(arr.length - arr.filter(Boolean).length).fill(0)])
result([0,1,0,3,0,12,0])    // [1, 3, 12, 0, 0, 0, 0]

@kingstone3 我用node运行的是可以,后面放到chrome果然结果不一样。。。没有safari。。。没有试具体结果

// 换成这样后,在chrome第一次是输出全部相反的顺序,再运行一次sort,才会把顺序变回来
const array = [0, 2, 0, 1, 4, 0, 0, 0, 5, 7, 8];
array.sort((fir, sec) => {
    return (fir === 0) ? 1 : -1
})
5SSS commented
function moveZero (arr) {
      let length = arr.length
      let index = 0
      while (index < length) {
        if (arr[index] === 0) {
          arr.push(0)
          arr.splice(index, 1)
        } else {
          index++
        }
        length--
      }
      return arr
    }
function out(arr){
    var len=arr.length;
    var stop=len;
    for(var i=0;i<len;i++){
        if(i===stop){
            break;
        }
        if(arr[i]===0){
            arr.splice(i,1);
            arr.push(0);
            i--;
            stop--;
        }    
    }
    return arr
}

这个题要求空间最小,不能创建新的数组。
一、思路:设置i和length动态保存指针,通过使用i和length这两个可变化的变化来保存指针。
索引到0的时候length向前移动,否则i向后移动,这样能保持循环的数组不会溢出。

 function moveZero(arry){
        var length = arry.length - 1
        for(var i=0;i<length;i++){
            while(arry[i] === 0){
                arry.push(arry.splice(i,1)[0])
                length --
            }
        }
        return arry
    }
  • 时间复杂度 O(n^2 - n),约等于n^2
    理论情况下都是 On,只不过实际情况下考虑数组偏移的成本。 就会变成O(n^2)了。
  • 空间复杂度O1
  • length-- 表示动态缩小length,防止向后添加数据长度超出,数组push一次,循环多一次,导致不会停止循环,浏览器奔溃。
  • splice会改变原来的数组,length本来就会-1,为什么还要length--呢?
    因为原来的数组所有数字往前推push,可能会跳过中间的数字,比如 1 2 0 3 4 5。 index = 2 的时候循环到0,把0放到最后,原来的数组变成了 1 2 3 4 5 0。我index=4的时候,3就被跳过去了。
  • splice 该方法会改变原始数组,向/从数组中添加/删除项目,然后返回被删除的项目,返回的项目放在一个数组里面。
    image
  • while
    while 循环会一直循环代码块,只要指定的条件为 true。
    在下面的例子中,循环中的代码将运行,一遍又一遍,只要变量(i)小于 10:
while (i < 10) {
    text += "数字是 " + i;
    i++;
} // 如果忘了对条件中使用的变量进行递增,那么循环永不会结束,条件永远满足,这会导致浏览器崩溃。

二、思路:通过设置i和j两个可变化的变量,来保存指针
设定一个慢指针i一个快指针j,快指针j每次+1, 当慢指针i的值不等于0的时候也往后移动,当慢指针i等于0并且快指针j不等于0的时候,交换快慢指针的值,慢指针i再+1

function moveZero(arr) {
  let i = 0
  let j = 0
  while (j < arr.length) {
    if (arr[i] !== 0) {
      i++
    } else if (arr[j] !== 0) {
      [arr[i], arr[j]] = [arr[j], arr[i]]
      i++
    }
    j++
  }
}
  • 时间复杂度 O(n)
  • 空间复杂度 O(1)
  • 设置j是为了防止向后添加数组长度超出去了
  • 在arr[i]等于0并且arr[j]不等于0的时候指针的值会采用交换策略。
  • 找到非0 i才往下走i++相当于慢指针,j无论什么情况下都会往前走j++相当于快指针,指针就有步长和步频,还有方向,他们步长都是1,方向相反。频率也不同。

三、扩展学习

(一)关于指针的学习

1、指针的概念

指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?

  • (1)指针的类型
    指针本身所具有的类型
  • (2)指针所指向的类型
    通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待
  • (3)指针的值-或者叫指针所指向的内存区或地址
    指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32 位程序里,所有类型的指针的值都是一个32 位整数,因为32 位程序里内存地址全都是32 位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为si zeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX 为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。
  • (4)指针本身所占据的内存区
    指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32 位平台里,指针本身占据了4 个字节的长度。指针本身占据的内存这个概念在判断一个指针表达式(后面会解释)是否是左值时很有用。

2、数组和指针的关系

参考文章

这个题要求空间最小,不能创建新的数组。
一、思路:设置i和length动态保存指针,通过使用i和length这两个可变化的变化来保存指针。
索引到0的时候length向前移动,否则i向后移动,这样能保持循环的数组不会溢出。

 function moveZero(arry){
        var length = arry.length - 1
        for(var i=0;i<length;i++){
            while(arry[i] === 0){
                arry.push(arry.splice(i,1)[0])
                length --
            }
        }
        return arry
    }
  • 时间复杂度 O(n^2 - n),约等于n^2
    理论情况下都是 On,只不过实际情况下考虑数组偏移的成本。 就会变成O(n^2)了。
  • 空间复杂度O1
  • length-- 表示动态缩小length,防止向后添加数据长度超出,数组push一次,循环多一次,导致不会停止循环,浏览器奔溃。
  • splice会改变原来的数组,length本来就会-1,为什么还要length--呢?
    因为原来的数组所有数字往前推push,可能会跳过中间的数字,比如 1 2 0 3 4 5。 index = 2 的时候循环到0,把0放到最后,原来的数组变成了 1 2 3 4 5 0。我index=4的时候,3就被跳过去了。
  • splice 该方法会改变原始数组,向/从数组中添加/删除项目,然后返回被删除的项目,返回的项目放在一个数组里面。
    image
  • while
    while 循环会一直循环代码块,只要指定的条件为 true。
    在下面的例子中,循环中的代码将运行,一遍又一遍,只要变量(i)小于 10:
while (i < 10) {
    text += "数字是 " + i;
    i++;
} // 如果忘了对条件中使用的变量进行递增,那么循环永不会结束,条件永远满足,这会导致浏览器崩溃。

二、思路:通过设置i和j两个可变化的变量,来保存指针

function moveZero(arr) {
  let i = 0
  let j = 0
  while (j < arr.length) {
    if (arr[i] !== 0) {
      i++
    } else if (arr[j] !== 0) {
      [arr[i], arr[j]] = [arr[j], arr[i]]
      i++
    }
    j++
  }
}
  • 时间复杂度 O(n)
  • 空间复杂度 O(1)
    设置j是为了防止向后添加数组长度超出去了,于是采用了交换策略。
    找到非0 i才往下走i++,找到0 j就往前走j++,指针就有步长和步频,还有方向,他们步长都是1,方向相反。频率也不同。

并不是找到0的时候j才往前走,是无论什么情况j都是在增加的,只有在arr[i]等于0并且arr[j]不等于0的时候指针的值会交换

function moveZero2(arr) {
  let zeroNum = 0;
  for(let i = 0; i < arr.length - zeroNum; i++) {
    if(arr[i] === 0) {
      zeroNum++
      arr.splice(i, 1)
      arr.push(0)
      i--
    }
  }

  console.log(arr)
}

上面所有的再循环中,先splice在推的方法都是有问题的
因为,当splice一个元素的时候,紧跟着的后面一个元素会向前移动一位,索引会变成正在删除那个元素的,所有当有连续的​​0时候,无法满足要求

那个这个问题怎么解决?没头绪

貌似在类似for循环里splice,都是一个不好的选择,后患无穷

function moveZero(array){
  let j = 0;
  for (let i = 0;i<array.length;i++){
    if(array[i]){
      array[j] = array[i];
      if(i!==j){
        array[i] = 0;
      }
      j++;
    }
  }
  return array;
}
function moveZero(arr){
	const len = arr.length;
	let flag = 0;
	for(let i=0; i< len - flag; i++){
		if(arr[i] == 0){
			arr.splice(i,1);
			arr.push(0);
			flag++;
			i--;
		}
	}
	return arr;
}

上面所有的再循环中,先splice在推的方法都是有问题的
因为,当splice一个元素的时候,紧跟着的后面一个元素会向前移动一位,索引会变成正在删除那个元素的,所有当有连续的​​0时候,无法满足要求

那个这个问题怎么解决?没头绪

索引减回去啊

从后往前循环找到0所在位置依次交换并记录0的个数减少交换次数

function zeroToLast(arr) {
			let index;  // 记录0所在的位置
			let count = 0;  // 记录有多少个0减少交换次数
			let len = arr.length;
			for(var i = len;i >=0;i --) {
				let item = arr[i];
				if(item === 0) {
					index = i;
					for(var j = index + 1;j < (len - count);j ++) {
						arr[j - 1] = arr[j];
					}
					count ++;
					arr[j - 1] = item;
				}
			}
			return arr;
}

let arr = [0,1,0,0,3,0,0,0,2,1,0,0];
function changeArr(arr) {
var len = 0;
arr.forEach(item=>{
if (item === 0){len++;}
})
arr = arr.filter(item=>{
if (item != 0) {
return item;
}
})
for(var i=0;i<len;i++){
arr.push(0);
}
return arr;
}
changeArr(arr);

没必要双指针,应该和快排**一样,用一个哨兵就好了。

let moveZero = (arr) => {
  let point = 0
  for (var i = 0; i < arr.length; i++) {
    if (arr[i] != 0) {
      let temp = arr[point]
      arr[point] = arr[i]
      arr[i] = temp
      point++
    }
  }

  return arr
}
var moveZeroes = function(nums) {
    let index = 0;
    let len = nums.length;
    for (let i = 0; i < len; i++) {
        if (nums[i] !== 0) {
            nums[index++] = nums[i];
        }
    }
    while (index < len) {
        nums[index++] = 0;
    }
}

var a = [0,0,1,0,0,3,12]

var max = a.length
for(var i = 0 ; i <max; i++){
if(a[i]===0){
a.push(...a.splice(i,1))
i--
max--
}
}

console.info(a)

const moveZeroes = nums => {
    let i = 0, j = 0, count = 0;
    while(j < nums.length) {
        if(nums[j] !== 0) {
            nums[i++] = nums[j];
        }
        j++;
    }
    while(i < nums.length) {
        nums[i++] = 0;
    }
};
let arr = [0, 1, 0, 0, 3, 12]
let index = 0
let zeroNum = 0
while(index !== arr.length - 1 - zeroNum ) {
    if (arr[index] === 0) {
        zeroNum++
        arr.splice(index, 1)
        arr.push(0)
        continue
    }
    index++
}
let arr = [0,0,0,1,2,0,0,0,0,7,9,0,0,567,0,0,0,4,6,8,0];

function moveZeroToLast(arr) {
  let brr =  arr.filter((item) => item !== 0 )// 去除为0 的元素
  const len = arr.length-brr.length;// 0 的个数
  return [...brr,...new Array(len).fill(0)]
}

console.log(arr);//[ 0, 0, 0, 1, 2, 0, 0, 0, 0, 7, 9, 0, 0, 567, 0, 0, 0, 4, 6, 8, 0 ]
console.log(moveZeroToLast(arr));//[ 1, 2, 7, 9, 567, 4, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
zo11o commented
/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var moveZeroes = function(nums) {
    let i = 0;
    let count = 0;
    while ( i < nums.length ) {
        if ( nums[i] === 0 ) {
            nums.splice(i, 1);
            count ++;
            
        } else {
            i ++;
        }
    }
    
    for (var j = 0; j < count ; j++) {
        nums.push(0)
    }
    
    return nums;
};

已经有人贴出了类似,而且看上去更简洁的代码了,不过还是把自己写出来的也贴一下。

const moveZero = (arr) => {
	let p;
	for (let i = 0, len = arr.length; i < len; i++) {
		if (arr[i] === 0 && p === undefined) {
			p = i; // find the first zero, set the pointer
		} 
		if (arr[i] !== 0 && p !== undefined) {
			arr[p] = arr[i]; // exchange the non zero element with the first zero
			arr[i] = 0;
			p++; // move pointer to next after exchanging
		}
	}
	return arr;
};
const arr=[0,1,0,3,0,0,0,12]
const movezero=(arr)=>{
  let count=arr.length
  for(let i=0;i<count;i++){
    if(arr[i]===0){
      arr.splice(i,1)
      arr[arr.length]=0
      i--
      count--
    }
  }
}
//  the first solution —— 出现数组或映射

const moveZero0 = arr => arr.filter(item => item !== 0).concat(arr.filter(item => item === 0));

console.log( moveZero0([1, 0, 2, 7, 0, 8, 0, '12']) );

//  the second solution —— 未做数组拷贝或映射

const moveZero = arr => {

    let j = arr.length;
    
    for(let i = 0; i < j; i ++) {

        if(arr[i] === 0) {

            arr.push(...arr.splice(i --, 1));
            j --;
        }
    }
    return arr;
}
console.log( moveZero([1, 0, 2, 7, 0, 8, 0, '12']) );
// 非0全部塞到前面原地inplace
var moveZeroes = function(nums) {
    var len = nums.length ;
    var last = 0;
    for( var i = 0 ; i < len ; i ++) {
        if(nums[i] != 0) {
            nums[last] = nums[i]
            ++last
        }
    }
    for(var j = last; j < len; j ++) {
        nums[j] = 0
    }
};
// 照上使用last记录非0序列下标
var moveZeroes = function(nums) {
    
    var last= 0 ;
    for(var i = 0 ; i < nums.length;i ++) {
        if(nums[i] !== 0 ) {
            swap(nums, last++, i)
        }
    }
    function swap(arr, i , j ) {
        var temp = arr[i]
        arr[i] = arr[j]
        arr[j] = temp
    }
};
function v(a){
    let count = 0;  // 记录0的个数

    for(let i = 0; i < a.length; i++){
        if(a[i] === 0){
            count++;
        }else if(count > 0){
            a[i - count] = a[i] // 向前移动
        }
        if(i > a.length - count - 1){
            // 末尾填充0
            a[i] = 0
        }
    }
}

//遍历数组一次,并且只在需要的时候才进行移动操作(交换0和非0)
function zeroFunc(arr) {
var zeroIndex = -1;
for (var i = 0, len = arr.length; i< len; i++) {
if (arr[i] === 0) {
if (zeroIndex === -1) { //记录第一个0的位置
zeroIndex = i;
}
} else if (zeroIndex !== -1) { //0的位置是 >= 0的,就进行换位
arr[zeroIndex] = arr[i];
arr[i] = 0;
zeroIndex++; // 上次zeroIndex+1的肯定是0
}
}
console.log(arr);
}

a=[0,1,0,3,12]
a.sort()
while(!a[0]){
a.push(a.shift())
}
前提是不能全是0

function translateZero(nums){
let len = nums.length;
let nums2 = nums.filter(num=>num!=0);
return nums2.concat('0'.repeat(len-nums2.length).split('').map(Number));
}

function parse(arr){
  return arr.filter((a)=>{ return a !==0 }).concat(arr.filter((a)=>{return a === 0}))
}

一行就搞定 连续0也没问题
原理:做两个减法然后进行连接

function moveZero(arr) {
    const zeroPositionIndex = arr.indexOf(0);

    if (zeroPositionIndex === -1 || arr.slice(zeroPositionIndex, arr.length).every(item => item === 0)) {
        return;
    } else {
        arr.splice(zeroPositionIndex, 1);
        arr.push(0);

        moveZero(arr)
    }
}
let arr=[0,1,0,3,12];
arr.forEach((v,i,arr)=>{
  if(v==0){
    arr.splice(i,1);
    arr.push(0)
  }
})
var arr = [1, 2, 3, 0, 0, 0, 4, 5, 0, 6]
for (var i = arr.length - 1; i >= 0; i--) {
   if (arr[i] === 0) {
   arr.push(0)
   arr.splice(i, 1)
   }
 }
 console.log(arr)

我看大多数答案,都反应了会splice数组以后,下次循环会少循环一次,所以遇到多个连续的0时会遇到问题,针对这个问题只需要让循环从最后一个开始就可以(有bug请求指教)( "i>=0" 修改一次,感谢btea )

var arr = [1, 2, 3, 0, 0, 0, 4, 5, 0, 6]
for (var i = arr.length - 1; i > 0; i--) {
   if (arr[i] === 0) {
   arr.push(0)
   arr.splice(i, 1)
   }
 }
 console.log(arr)

我看大多数答案,都反应了会splice数组以后,下次循环会少循环一次,所以遇到多个连续的0时会遇到问题,针对这个问题只需要让循环从最后一个开始就可以(有bug请求指教)

大佬,厉害了

btea commented
var arr = [1, 2, 3, 0, 0, 0, 4, 5, 0, 6]
for (var i = arr.length - 1; i > 0; i--) {
   if (arr[i] === 0) {
   arr.push(0)
   arr.splice(i, 1)
   }
 }
 console.log(arr)

我看大多数答案,都反应了会splice数组以后,下次循环会少循环一次,所以遇到多个连续的0时会遇到问题,针对这个问题只需要让循环从最后一个开始就可以(有bug请求指教)

第一个参数为0没有移动

var arr = [1, 2, 3, 0, 0, 0, 4, 5, 0, 6]
for (var i = arr.length - 1; i > 0; i--) {
   if (arr[i] === 0) {
   arr.push(0)
   arr.splice(i, 1)
   }
 }
 console.log(arr)

我看大多数答案,都反应了会剪接数组以后,下次循环会少循环一次,所以遇到多个连续的0时会遇到问题,针对这个问题只需要让循环从最后一个开始就可以(有错误请求指教)

第一个参数为0没有移动

感谢( i>0 改为 i>=0)

    var arr = [0,1,0,3,12];
    function moveZero(arr){
        var len = arr.length,
            zeroNums = 0;

        for(let i = 0; i < len; i++){
            if(arr[i - zeroNums] === 0){
                arr.push(...arr.splice(i - zeroNums++,1));
            }
        }
    }
    moveZero(arr);
    console.log(arr);
const nums = [0, 0, 1, 0, 0, 3, 12];
for (let i = 0, len = nums.length; i < len; ) {
  if (nums[i] === 0) {
    nums.splice(i, 1);
    len--;
    nums.push(0);
  } else {
    i++;
  }
}

console.log(nums);
  function moveZeros(arr) {
	let i = 0, len = arr.length;
            // 不管如何循环一个数组的长度...
	while(len--) {
		if (arr[i] === 0) {
			arr.splice(i, 1);
			arr.push(0);
		} else {
			i++;
		}
	}
   }
   
let arr1 = [0,1,0,0,0,0,0,0,0,5,0,0,0,0,3,12];
moveZeros(arr1);
console.log(arr1);

function zeroMove(arr){
var j=arr.length;
for(let i=0;i<j;i++){
if(arr[i]===0){
arr.push(arr[i]);
arr.splice(i,1);
i--;
j--;
}
console.log(i);
}
}

    test=(arr) => {
        let [_str, str] = ['', arr.toString()]
        const match = [...str.matchAll(/(^0,)|(,0)/g)]
        _str = str.replace(/,0/g, '').replace(/^0,/, '') + ',0'.repeat(match.length)
        return _str.split(',')
    }

哈哈哈,这样算不算离题了啊

求大佬斧正

function answer(arr) {
  for (let i = arr.length - 1; i >= 0; i--) {
    if (arr[i] === 0) {
      arr.splice(i, 1)
      arr.push(0)
    }
  }
}
// 创建一个随机数组
function createRandomArray() {
	let arr = [];
	for(let i=0; i<10; i++) {
		arr.push(Math.floor(Math.random() * 4))
	}
	return arr
}

var arr = createRandomArray();
console.log(arr);
arr.sort((a, b) => b === 0 ? -1 : a === 0 ? 1 : 0)

var a = [0,1,0,3,12]
var len = a.length
var b = 0
for (let i = 0; i < len - b; ) {
if (a[i] === 0) {
a.push(a.splice(i, 1)[0])
b++
}
else i++
console.log(i)
}
console.log(a)

var moveZeroes = function(nums) {
    if(!Array.isArray(nums)){
        return ;
       }
    
    if(nums.length == 0){
       return [];
       }
    let index = 0;
    for (let i = 0; i < nums.length; i++) {
      if (nums[i] !== 0) {
        let temp = nums[index];
        nums[index] = nums[i];
        nums[i] = temp;
        index++;
      }
    } 
    return nums; 
};

let arr = [0,1,0,3,12,0,45,0,9,0];
arr.sort(function (a,b) {
return a-b;
})
for (let i = 0;i<arr.length;i++){
if(arr[i] === 0){
arr.shift();
i = -1;
arr.push(0);
}else{
break;
}
}
console.log(arr)

let arr = [0, 0, 1, 4, 0, 0, 8 ,7 ,5 , 5, 0, 0];
function arry(array) {
let le = array.length;
for(let i = 0; i<le ; i++){
if(array[i] == 0){
array.splice(i, 1);
array.push(0);
}
}
return array
}
console.log(arry(arr));

这是一个leetcode原题,

以下是我的参考答案:

var moveZeroes = function(nums) {
    let index = 0;
    for(let i = 0; i < nums.length; i++) {
        const num = nums[i];
        if (num !== 0) {
            nums[index++] = num;
        }
    }

    for(let i = index; i < nums.length; i++) {
        nums[index++] = 0;
    }
};

详细思路参考leetcode 题解 - 283.move-zeroes

arr.sort((a, b) => {
if (a === 0) return 1
return -1
})

    function moveZero (arr) {
      const arr1 = []
      while (arr.includes(0)) {
        arr.splice(arr.indexOf(0), 1)
        arr1.push(0)
      }
      arr.push(...arr1)
      return arr
    }

    console.log(moveZero([0,1,0,3,12]))
let list = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9];
list.sort((a, b) => a - b);
for (let i = list.length - 1; i >= 0; i--) {
    const item = list[i];
    if (item == 0) {
        list.splice(i, 1);
        list.push(0);
    }
}
console.log(list);

var a = [0,0,10,9,5,11,0,22,100,0,1]
function bc(arr, count=0){
if(arr.indexOf(0) > -1){
arr.splice(arr.indexOf(0), 1)
bc(arr, count+1)
}else{
Array.from({length: count}, x => arr.push(0))
}
}
bc(a);
console.log(a) //  [10, 9, 5, 11, 22, 100, 1, 0, 0, 0, 0]

简单实现功能
Array.prototype.mySort = function() {
this.sort((a, b) => a - b); // 排序
while (this[0] === 0) {
// this.splice(0, 1).push(0);
this.splice(0, 1);
this.push(0);
}
return this; // 是为了 防止直接执行打印 返回 undefined
}
var a = [0, 1, 2, 3, 3134, 5, 16, 7, 28, 9, 0 , 1 , 0, 8]
// console.log(a.mySort()) // 不加最后一句 直接执行会打印undefined 所有方法最好加了 return this
a.mySort();
console.log(a) // [1, 1, 2, 3, 5, 7, 8, 9, 16, 28, 3134, 0, 0, 0]

arr.sort((a, b) => (a === 0 && 1) || (b === 0 && -1) || a - b)
function moveToEnd(arr) {
    let len = arr.length;
    let inx = 0;
    if (len > 1) {
        while (inx < len) {
            if (arr[inx] === 0) {
                arr.push(arr.splice(inx, 1)[0]);
                len--;
            } else {
                inx++;
            }
        }
    }
}
    var a = [0, 1, 0, 3, 12]
    for (let i = a.length - 2; i >= 0; i--) {
      const temp = a[i]
      if (temp === 0) {
        a.splice(i, 1)
        a.push(temp)
      }
    }

至少 少一次操作

        const moveZore = (arr) => {
            let n = 0
            arr.forEach((item, index) => {
                if (item === 0){
                    arr.splice(index, 1)
                    n++;
                }
            })
            arr.push(...(new Array(n)).fill(0))
            return arr;
        }

试试这个数组 let arr = [1, 2, 3, 0, 0, 2, 0, 4, 0, 0, 0];
结果会出问题的

   let arr = [0,1,0,2,0,3,12,0,0];
    // 可以避免本身最后就是0时的操作
   function moveZero(arr) {
	let flag, index, count = 0;
	let len = arr.length;

	for(let n = 0; n < len; n++) {
		if(arr[n] === 0) {
			count++;
		}
	}

	for(let i = 0; i < count; i++) {
		index = arr.indexOf(0);
		if(index > -1 && arr[len - i - 1] !== 0) {
			arr.push(arr.splice(index, 1)[0]);
		}
	}
  };
  moveZero(arr);
  console.log(arr); // [ 1, 2, 3, 12, 0, 0, 0, 0, 0, 0 ]
var arr=[0,1,0,3,12];
for (let i = 0; i< arr.length; i++){
    arr[i]?'':arr=arr.concat(arr.splice(i,1))
}
console.log(arr)
// 用es6中提供的函数,实现起来会比较方便
const arr = [0,1,0,0,0,0,1,3,12];
let res = arr.filter(e => e>0);
res = res.concat(Array.from({length: arr.length - res.length}).fill(0))

题目要求 尽量减少操作次数。
上面已经有人给出没有splice的双指针法了,不过用了交换数组的方法,复杂度为O(2n)(一般计算的时候常数忽略不计,所以可以当作O(n))
举例

const arr = [1,0,2,3,4];

function moveZeroToLast(arr) {
    let index = 0;
    for (let i = 0, length = arr.length; i < length; i++) {
        if (arr[i] === 0) {
            index++;
        } else if (index !== 0) {
            arr[i - index] = arr[i];
            arr[i] = 0;
        }
    }
    return arr;
}

moveZeroToLast(arr); // 此时0需要从第二位一直交换到最后一位,即操作了8次

如果在循环的时候不交换,只将数组元素替换到0的位置,最后用0填充剩下的位置

// 第一次 [1,2,2,3,4]
// 第二次 [1,2,3,3,4]
// 第三次 [1,2,3,4,4]
// 第四次 [1,2,3,4,0]

const arr = [1,0,2,3,4];

function moveZeroToLast(arr) {
    let index = 0;
	let length = arr.length;

    for (let i = 0; i < length; i++) {
        if (arr[i] !== 0) {
            arr[index] = arr[i];
            index++;
        }
    }

	for(; index < length; index++) {
		arr[index] = 0;
	}
    return arr;
}

moveZeroToLast(arr);

时间复杂度为O(n + m), m 为 0 的个数。
因为 0 的个数小于数组的长度,所以 m < n, 那么 O(n + m) < O(2n)。
具体的复杂度还得看 0 的出现的顺序和个数,这里只提供一种我认为可能尽量减少操作次数的方法。

双指针

function displacement (arr) {
  let i = 0;
  for (let j = 0, len = arr.length; j < len; j++) {
    if (arr[j] !== 0) {
      [arr[i], arr[j]] = [arr[j], arr[i]]
      i++;
    }
  }
  return arr;
}
let arr = [0, 1, 0, 0, 12, 0, 0, 1, 0, 0];

function moveZero(arr) {
    let i = 0;
    let k = 0;
    let j = arr.length - 1;
    while (k < (arr.length - 1)) {
        if (arr[i] == 0) {
            arr.push(arr[i]);
            arr.splice(i, 1);
            j--;
        } else {
            i++;
        }
        k++;
    }
    return arr;
}

console.log(moveZero(arr))

复杂度为O(n).应该是最低的了

数组+字符串+正则 来回切换。无需遍历

function zeroArr(arr){
	const reg = /0/g
	const arrToStr = arr.join('')
	const len = arrToStr.match(reg).length
	return (arrToStr.replace(reg,'') + (Math.pow(10,len)+'').substring(1,len+1)).split('')
}
zeroArr([0,1,2,3,0,5])

arr.sort((a,b)=>{return a*b ? a-b : b-a;});

上面所有的再循环中,先 splice 在 push 的方法都是有问题的

因为,当splice 一个元素的时候,紧跟着的后面一个元素会向前移动一位,索引会变成正在删除那个元素的,所有当有连续的 0 时候,无法满足要求

逆向思维,解决你的烦恼
function fn(arr, v){
for(var i = arr.length-1; i>=0; i--){
if(arr[i] === v) {
arr.splice(i, 1);
arr.push(v)
}
}
return arr
}

数组+字符串+正则 来回切换。无需遍历

function zeroArr(arr){
	const reg = /0/g
	const arrToStr = arr.join('')
	const len = arrToStr.match(reg).length
	return (arrToStr.replace(reg,'') + (Math.pow(10,len)+'').substring(1,len+1)).split('')
}
zeroArr([0,1,2,3,0,5])

你似乎忘记了10、120这些类似的情况.

function moveZero(list) {
  let count = 0;
  for(let i=0;i<list.length;i++){
    if(list[i] === 0){
      list.splice(i, 1)
      count++
      i--
    }
  }
  return [...list, ...new Array(count).fill(0)]
}

[0,1,0,3,12].filter(v=>v!=0).concat([0,1,0,3,12].filter(v=>v==0))
先将0过滤掉,再追加到最后

filter()会生成新数组,split() 也会生成新数组,都不是从原数组中进行操作。sort()无法保证排序的时间和空间复杂性~,怎么感觉这么~~~

let n = 0;
for (var i = 0; i < arr.length - n; i++) {
  if (arr[i] === 0) {
    arr.push(Number(arr.splice(i, 1)))
    n++
    i--
  }
}

至于上面提到的双指针,是个好东西,学到了。

利用 indexOf 不需要遍历数组

function moveZero(arr){
let zeroCount=0;
let findIndex=0;
var index;
while(index=arr.indexOf(0, findIndex), index!==-1){
arr.splice(index, 1);
zeroCount++;
}
let zeroArr = Array.from({length:zeroCount}).fill(0);
return arr.concat(zeroArr);
}
let arr=[0,1,0,3,12];
moveZero(arr)

利用indexOf不需要遍历数组

function moveZero(arr){
let zeroCount = 0;
让findIndex = 0;
var index;
while(index = arr.indexOf(0,findIndex),index!== - 1){
arr.splice(index,1);
zeroCount ++;
}
let zeroArr = Array.from({length:zeroCount})。fill(0);
return arr.concat(zeroArr);
}
let arr = [0,1,0,3,12];
moveZero(ARR)

你貌似没理解遍历是什么意思,而且你这里不止是遍历了一次。

引用mdn: Array.from() 方法从一个类似数组或可迭代对象中创建一个新的,浅拷贝的数组实例。

引用mdn: concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

最后看一眼你的原数组打印。

console.log(arr) // [1, 3, 12]
/*
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:

必须在原数组上操作,不能拷贝额外的数组。

尽量减少操作次数
*/

function moveZero(arr) {
  let index = 0;
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] !== 0) {
      arr[index++] = arr[i];
    }
  }
  for (let i = index; i < arr.length; i++) {
    arr[i] = 0;
  }
  return arr;
}
console.log(moveZero([0, 1, 0, 3, 12]));