题目
题目来源:力扣(LeetCode)
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1]
输出:[0,1,2]
示例 3:
输入:nums = [0]
输出:[0]
示例 4:
输入:nums = [1]
输出:[1]
思路分析
我们借助快速排序的思想,对数组进行一个分区操作。分别定义一个循环不变量 i 及两个边界量 zero 和 two,其中:
- 变量 i 是循环变量,一般设置为开区间,表示 i 之前的元素都是遍历过的;
- 变量 zero 是前两个子区间的分界点,一个是闭区间,另一个就必须是开区间;
- 变量 two 是另一个分界线,设计成闭区间;
- len 是数组的长度;
则有:
- 子区间 [0, zero) 存储所有的 0;
- 子区间 [zero, i) 存储所有的 1;
- 子区间 [two, len - 1) 存储所有的2;
我们遍历数组,如果当前遍历的数字为 0,则将当前的数字与 zero 所在的元素进行交换;如果数字为 2 ,则将当前的数字 2 与 two 所在的元素进行交换。最终遍历完数组后,数组已经排序好。
/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
var sortColors = function(nums) {
const len = nums.length;
if (len < 2) return
const swap = (arr, i, j) => [arr[i], arr[j]] = [arr[j], arr[i]];
// 子区间 [0, zero) 存储所有的 0
// 子区间 [zero, i) 存储所有的 1
// 子区间 [two, len - 1] 存储所有的2
// 循环终止条件是 i == two,那么循环可以继续的条件是 i < two
// 为了保证初始化的时候 [0, zero) 为空,设置 zero = 0
// 所以下面遍历到数字是 0 的时候,元素先交换,然后循环变量再加
let zero = 0;
// 为了保证初始化的时候 [two, len - 1] 为空,设置 two = len
// 所以下面遍历到的元素是 2 的时候,边界量two先减,然后元素再交换
let two = len ;
let i = 0;
while(i < two) {
switch(nums[i]) {
case 0: // 元素先交换,然后循环变量再加
// swap(nums, zero++, i)
// i++
swap(nums, i, zero)
zero++
i++
break;
case 1:
i++
break;
case 2: // 边界量two 先减,然后再交换元素
// swap(nums, two--, i)
two--
swap(nums, i, two)
break;
default:
break;
}
}
};