题目
- 如输入 [1, 0, 3, 0, 11, 0],输出 [1, 3, 11, 0, 0, 0]
- 只移动0,其他顺序不变
- 必须在原数组进行操作
传统思路
- 遍历数组,遇到0则push到数组末尾
- 用 splice 截取掉当前元素
- 时间复杂度 O(n^2) ——算法不可用
优化思路 - 双指针
- 定义一个变量zeroIndex,用来记录第一个0的位置
- 遍历数组,zeroIndex找到第一个0,i找到zeroIndex后面第一个非0
- 交换位置,继续向后移动
- 之遍历一次,所以时间复杂度是O(n)
/**
* @description 移动 0 到数组末尾
* @author lsr
*/
/**
* 移动 0 到数组末尾 - 嵌套循环
* @param arr
* @returns
*/
export function moveZero1(arr: number[]) {
let length = arr.length
if (length === 0) return
for (let i = 0; i < length; i++) {
if (arr[i] === 0) {
arr.push(0)
arr.splice(i, 1)
// 排除末尾0的循环,否则会进入死循环
length--
// 截取之后,i--,重新判断当前值是否非0
i--
}
}
}
/**
* 移动 0 到数组末尾 - 双指针
* @param arr
* @returns
*/
export function moveZero2(arr: number[]) {
const length = arr.length
if (length === 0) return
let zeroIndex = -1 // 0的索引
for (let i = 0; i < length; i++) {
// 找到第一个0
if (arr[i] === 0 && zeroIndex < 0) {
zeroIndex = i
}
// 找到第一个非0
if (arr[i] !== 0 && zeroIndex >= 0) {
// 与0交换位置
const n = arr[i]
arr[i] = arr[zeroIndex]
arr[zeroIndex] = n
// 指向下一个0
zeroIndex++
}
}
}
// 功能测试
// const arr1 = [1, 0, 3, 0, 11, 0]
// moveZero1(arr1)
// console.log(arr1)
// const arr2 = [1, 0, 3, 0, 11, 0]
// moveZero1(arr2)
// console.log(arr2)
// 性能测试
const arr1 = []
for (let i = 0; i < 20 * 10000; i++) {
if (i % 10 === 0) {
arr1[i] = 0
} else {
arr1[i] = i
}
}
console.time('moveZero1')
moveZero1(arr1)
console.timeEnd('moveZero1') // 178.4111328125 ms
const arr2 = []
for (let i = 0; i < 20 * 10000; i++) {
if (i % 10 === 0) {
arr2[i] = 0
} else {
arr2[i] = i
}
}
console.time('moveZero2')
moveZero2(arr2)
console.timeEnd('moveZero2') // 1.7041015625 ms
单元测试
/**
* @description 移动 0 到数组末尾 test
* @author lsr
*/
import { moveZero1, moveZero2 } from '@/01-algorithm/move-zero'
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, 1, 1, 1, 1, 1]
moveZero2(arr)
expect(arr).toEqual([1, 1, 1, 1, 1, 1])
})
it('全是0', () => {
const arr = [0, 0, 0, 0, 0, 0]
moveZero2(arr)
expect(arr).toEqual([0, 0, 0, 0, 0, 0])
})
})
划重点
- 审题:是否必须修改原数组
- 数组是连续存储,慎用splice、unshift 等API
- 双指针思路