记录 1 道算法题
颜色分类
要求:
输入:[2,0,2,1,1,0] 输出:[0,0,1,1,2,2]
0在最前面,1在中间,2在最后面。
需要进行排序,指针是比不可少的,简单的做法是把 0 全部换到左边,然后把 2 换到右边,那自然就完成排序。这是双指针的做法,一个在头一个在尾。但是如果是这样的话,每次移动尾指针要进行一个循环,找到尾部非 2 的下标。不然比如 [2,1,2],[0,2,2,2,0,2,1,1] 就不行。
单指针的做法则是进行两次遍历。先将 0 全部换到左边,然后把 2 换到右边。不过当我们把 0 归位的时候,其实我们的指针是指着 1 的开始位置,所以也可以将 1 换到这个位置。
首先提供一个交换位置的方法。
- 单指针
function swap(arr, i, j) {
const t = arr[i]
arr[i] = arr[j]
arr[j] = t
}
function sortColors(nums) {
let j = 0
for(let i = 0; i < nums.length; i++) {
const num = nums[i]
if (num === 0) {
swap(nums, i, j)
j++
}
}
for(let i = j; i < nums.length; i++ {
const num = nums[i]
if (num === 1) {
swap(nums, i, j)
j++
}
}
return nums
}
- 双指针
先来个归类 0 和 2 的写法。问题是每次交换后都可能导致 nums[i] 会满足另一个条件。所以还得判断。
function sortColors(nums) {
let j = 0
let k = nums.length - 1
while (nums[k] === 2) {
k--
}
for (let i = 0; i <= k; i++) {
const num = nums[i]
if (num === 2) {
swap(nums, i, k)
// 每次交换完之后 nums[i] 都可能是 0
if (nums[i] === 0) {
swap(nums, i, j)
j++
}
k--
// 尾部指针 k-- 很可能指着 2
while (nums[k] === 2) {
k--
}
}
if (num === 0) {
swap(nums, i, j)
// 同样每次交换完之后都可能是 2
if (num[i] === 2) {
swap(nums, i, j)
k--
// 同样尾指针要跳过 2
while (nums[k] === 2) {
k--
}
}
j++
}
}
return nums
}
上面这一版可能不怎么精简,但执行效率还行,精简一点的写法就是尽可能减少交换之后 nums[i] 可能会出现满足另一个条件的概率。我们可以试一下归类 0 和 1。因为如果 0 指针移动了, 1 指针也要跟着移动,而每次移动后,左边都是已经确定的值,所以会减少 nums[i] 出现奇怪的数字。因为 0 和 1 是相邻的,所以不需要像 2 那样,中间一定要隔着东西。
function sortColors(nums) {
let j = 0
let k = 0
while(let i = 0; i < nums.length; i++) {
const num = nums[i]
if (num === 1) {
swap(nums, i, k)
k++
} else if (num === 0) {
swap(nums, i, j)
// 当两个指针不是在一起时,说明已经确定第一个 1
// 0 的下一个位置指针可能指向 1
// 所以交换 0 的时候可能会把第一个 1 给换走,
// 所以要换回来
// 比如 [2,0,2,1,1,0]
if (j < k) {
swap(nums, i, k)
}
j++
k++
}
}
}