leetcode75. 颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:
不能使用代码库中的排序函数来解决这道题。
示例:
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
进阶:
- 一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。 - 你能想出一个仅使用常数空间的一趟扫描算法吗?
方法:双指针
思路:
本题容易想到的是快速排序的想法将数组进行切分,但是需要注意的是,直接使用快速排序,时间复杂度是O(nlogn)。
刚开始的时候,我思考是否可以只进行快速查找的一步,就可以完成排序,因为数组中只有0、1、2,那么以1位分界点是否可以一次成功呢?答案是不可以。因为进行快速查找之后(使用常数空间的快速查找),只可以保证区间分成两部分,左边区间的数都小于等于1,右边区间的数都大于等于1,但是不代表所有的1都在中间,需要继续分治进行快速查找才可以。
我们使用类似的双指针的方法进行划分,我们希望最后将数组分为以下三个部分:
- 第一部分为
[0,first)
,左开右闭,这个部分全为0; - 第二部分为
[first,second)
,左开右闭,这个部分全为1; - 第三部分为
[second,n)
,左开右闭,这个部分全为2。
我们初始化指针first=0,second=n,这时初始第一第三部分区间为空。我们不断更新两个指针来更改第一和第三区间的位置。
遍历的时候,我们使用cur表示当前遍历到的下标,那么nums[cur]可能出现三种情况:
- nums[cur]=0的时候,此时第一部分区间应该扩大,因此将cur和first对应的数值交换,然后first++,cur++继续遍历;
- nums[cur]=1的时候,此时cur在第二个区间中,因此两个指针不需要变化,cur++;
- nums[cur]=2的时候,此时第三部分区间应该扩大,由于区间是左闭右开的,先将second–,然后交换cur和second对应的位置。此时不需要cur++,因为second一定位于cur之后,交换之后该值还未遍历过,继续判断该值。
- 最后cur=second的时候,即将所有的值遍历了一遍,结束。