【Sort】75. Sort Colors(计数排序、荷兰国旗问题)

来源:互联网 发布:百战天虫java 编辑:程序博客网 时间:2024/05/01 23:57

Given an array with n objects colored red, white or blue, sort them so that objects of the same color are adjacent, with the colors in the order red, white and blue.

Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.

Note:

You are not suppose to use the library's sort function for this problem.

Follow up:
A rather straight forward solution is a two-pass algorithm using counting sort.
First, iterate the array counting number of 0's, 1's, and 2's, then overwrite array with total number of 0's, then 1's and followed by 2's.

Could you come up with an one-pass algorithm using only constant space?

当然可以像这样一下就AC。。。。

class Solution {public:    void sortColors(vector<int>& nums) {        sort(nums.begin(),nums.end());    }};

但是这个题想考什么呢。。。这里提到了counting sort(计数排序)

这是一种非比较排序算法,适用于待排序的数范围不大并且有很多重复的情况。算法思想很简单:如果知道了一堆数中有多少个数比a大,也就知道了a的位置(a是某个元素)。但是怎么通过非比较的方式去统计有多少个数比a大呢?答案是通过和数组下标建立映射关系。

假如有一堆数:0,0,0,1,1,2,2,2,2存在A数组里,现在考虑C数组:

C[0]、C[1]、C[2]、C[3]、C[4]、C[5]、C[6]、C[7]、C[8]、C[9]

初始化它们为0。然后我们遍历A,用C[A[i]]+=1来统计A[i]出现的次数(注意这里把待排序的A[i]转换成了C的下标),得到:

C[0]、C[1]、C[2]、C[3]、C[4]、C[5]、C[6]、C[7]、C[8]、C[9]

3、2、4、0、0、0、0、0、0

这个过程也反映了统计排序的一个劣势:如果待排序的数范围很大,那么空间的开销将非常大!

现在我们遍历C,做C[i]+=C[i-1],这就得到了有多少个小于等于A[i]的数的个数。

最后我们用一个数组B来保存结果,遍历A,做:B[C[A[j]]]=A[j]; C[A[j]]--;也就是找到A[j]合适的位置,把A[j]放进去,同时减小负责计数的C。这里虽然看上去复杂,但其实意思非常清晰:A[j]表示这个待排序的数,也是C的下标,C[A[j]]表示A[j]前面有多少个数小于等于它,那么C[A[j]]就是A[j]在B中的新位置(因为是小于等于,它自己也被包括了,所以位置是C[A[j]])。

以上就是计数排序的思路。

所以这个题就这样解决啦:

class Solution {public:    void sortColors(vector<int>& nums) {        vector<int> counter(3,0);        vector<int> res(nums.size(),0);        for(int i=0;i<nums.size();i++)            counter[nums[i]]++;//count the number of 0s,1s and 2s        for(int i=1;i<counter.size();i++)            counter[i]+=counter[i-1];//now counter[i]=the number of elements greater than or equal to nums[i]        for(int i=nums.size()-1;i>=0;i--){            res[counter[nums[i]]-1]=nums[i];            counter[nums[i]]--;        }        nums=res;    }};
需要注意。。。上面的过程参考了《算法导论》,A的下标是从1开始的,但实际编程下标都是从0开始,所以

res[counter[nums[i]]-1]=nums[i];
这里要减1。
这个题的follow-up很有意思:

如何one-pass解决掉它?

class Solution {public:    void sortColors(vector<int>& nums) {        int j=0,k=nums.size()-1;        for(int i=0;i<=k;i++){            if(nums[i]==0)                swap(nums[i],nums[j++]);            else if(nums[i]==2)                swap(nums[i--],nums[k--]);        }    }};
这其实就是大名鼎鼎的荷兰国旗问题了(Dutch Nation Flag Problem),可以参考:https://en.wikipedia.org/wiki/Dutch_national_flag_problem