🎗️ 主页:小夜时雨
🎗️专栏:快速排序
🎗️如何活着,是我找寻的方向
1. 题目解析
题目链接: https://leetcode.cn/problems/sort-colors/description/
本道题是快速排序的核心代码区间, 后面的快速排序会用到这一思想,所以还是十分重要的, 接下来我们来分析一下这道题目.
- 首先我们注意到这个要求在原地进行排列, 若是使用辅助数组, 我们总头开始遍历原数组, 以此先找出所有的 0 放在辅助数组中, 之后是找到所有的 1 放在辅助数组中 0 的后面, 最后是数字 2.
- 这样算下来的时间复杂度则是O(N ^ 2), 题目要求是在原地进行, 所以这个方法是行不通的, 而且时间复杂度有点高, 接下来我们介绍一下三指针的用法,只需要遍历原数组一边,即可完成区域的划分.
具体实现过程:
-
创建三个变量 i, left, right, i 表示遍历原数组, left 用来标记排好之后 0 区域的最右侧, right 标记 2 区域的最左侧。
-
标记原因:排好之后 0 是在最左侧, 所以我们找到 0 区域的最右侧,那么 left 的左边区域都是 0, 右侧都是1, 同理 right 的右侧都是 2, 左侧都是 1,见下图更容易理解。
-
这样标记之后, 发现数组被分成了四个区域,[0, left] 这个位置都是 0, [left + 1, i] 这个区域都是 1, [i, right - 1] 是带扫描的区域,[right, n - 1] 都是 2 (n 是原数组的长度)。
-
扫描到 i 位置的时候, 需要进行判断 i 的值:
1). nums[i] = 0, 那么需要把 0 放到 0 的区域, 那么也就是 left + 1 的后面, 此时只需要交换 left + 1 和 i 位置的值即可, 交换之后, left 和 i 同时向后加 1,即是swap(++left, i++, nums);
2). nums[i] = 1,我们注意到 i 的左侧区域就是 [left + 1, i] 那么也就是 1 的区域, 此时只需要 i++ 即可。
3). nums[i] = 2,right - 1 右侧的区域都是 2, 需要交换 right-1位置和 i 位置的值,注意:
- 交换之后,right - 1位置原本的值我们是还没有进行判断的,不同于 nums[i] = 0,那个其实交换的是 nums[++left] = 0 和 nums[i] = 1, 交换的本质就是 0 和 1 位置的交换, 我们已经知道 nums[++left] = 1了。
- 但是此处我们是不知道 nums[right - 1] 的值的,所以交换之后 right-1, 注意 i 要不变,不能向后加 1, 还需要再次进行判断原来 right -1 位置的值。
- 所以代码是如下的写法: 而没有 i + 1。
swap(–right, i, nums); 里面的是 - - right
接下来的代码里还会再次进行强调的,看下面的图更容易理解:
2. 代码
看下面的代码对照着上面的流程解析可能会更加的清楚。
public void sortColors(int[] nums) {
int n = nums.length;
// left 标记 0 区域的最右侧,0 位置还没有遍历,是 i 的位置,所以left 从 -1 开始
// right 标记 2 区域的最左侧, 也就是 n 的位置,i 遍历不到
int left = -1, right = n;
// [i, right - 1] 是带扫描区域, 当 i == right 的时候也就是 i 扫描完了
for(int i = 0; i < right; ) {
if(nums[i] == 0) {
// left 标记0区域的最右的一个位置
swap2(++left, i++, nums);
} else if(nums[i] == 1) {
// 中间的区域直接跳过
i++;
}else {
// right 标记2区域的最左侧位置
// 注意这个位置没有 i++
swap2(--right, i, nums);
}
}
}
private void swap2(int left, int right, int[] nums) {
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
}
后面我们会讲解快速排序的实现流程。
🎗️🎗️🎗️ 好啦,到这里有关本题的分享就没了,如果感觉做的还不错的话可以点个赞,关注一下,你的支持就是我继续下去的动力,我们下期再见,拜了个拜~ ☆*: .。. o(≧▽≦)o .。.:*☆