Leetcode第75题:颜色分类(荷兰国旗问题)

1 问题描述

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

注意:
不能使用代码库中的排序函数来解决这道题。(真香警告.jpg)

示例:

输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]

进阶:

  • 一个直观的解决方案是使用计数排序的两趟扫描算法。
    首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
  • 你能想出一个仅使用常数空间的一趟扫描算法吗?

2 解题思路

2.1 快速排序的变种

荷兰国旗问题,基本上每本算法书上都会提到,最常见效率也最高的就是快速排序的变种,算法思想跟快速排序很类似,中间的是某个元素,左边都比该元素小,右边都比该元素大。
定义三个指针left=-1,mid=0,right=nums.size。
mid从头到尾遍历nums
遇到0,mid与left位置的元素交换,++left,因为left 遇到2,mid与right位置的元素互换,–right,mid>=right为循环结束条件,mid无需自增
遇到1,1在中间mid位置很正常,继续向后,++mid
这种算法思想的时空复杂度为O(n)和O(1),而且适用性更好,即便换成超过3种元素的数组依然适用。

2.2 计数排序

定义一个长度为3的数组,扫描一次nums记录0、1、2的个数,然后依据这个数组重新对num进行赋值。
这种算法思想的时空复杂度为O(n)和O(1)。

3 代码

3.1 快速排序的变种

3.1.1 C++

执行结果:0ms,100%。
(自带函数大法好,swap函数写的都比我效率高)

class Solution {
public:
    //荷兰国旗问题
    void sortColors(vector<int>& nums) {
        //荷兰国旗问题
        //快速排序的变种
        //中间为1,左边为0都比1小,右边为2都比1大
        int left=-1;
        int mid=0;
        int right=nums.size();
        
        /*
        *mid从头到尾遍历nums
        *遇到0,mid与left位置的元素交换,++left,因为left=right为循环结束条件,mid无需自增
        *遇到1,1在中间mid位置很正常,继续向后,++mid
        */
        while(mid<right){
            if(nums[mid]==0)
                swap(nums[++left],nums[mid++]);
            else if(nums[mid]==2)
                swap(nums[mid],nums[--right]);
            else //if(nums[mid]==1)
                ++mid;
        }
        
        return;
    }
};

3.1.2 Python

执行结果:44ms,99.92%。
这个len()不是类里面的方法用着真的不习惯,而且python也不支持++/–,还有就是swap方式总感觉怪怪的…

class Solution:
    def sortColors(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        #荷兰国旗问题
        #快速排序的变种
        #中间为1,左边为0都比1小,右边为2都比1大
        left=-1
        mid=0
        right=len(nums)
        
        '''
        mid从头到尾遍历nums
        遇到0,mid与left位置的元素交换,++left,因为left=right为循环结束条件,mid无需自增
        遇到1,1在中间mid位置很正常,继续向后,++mid
        '''
        while mid<right:
            if nums[mid]==0:
                left+=1
                nums[left],nums[mid]=nums[mid],nums[left]
                mid+=1
            elif nums[mid]==2:
                right-=1
                nums[mid],nums[right]=nums[right],nums[mid]
            else:#nums[mid]==1
                mid+=1
        
        return;

3.2 计数排序

3.2.1 C++

执行结果:4ms,84.14%。

class Solution {
public:
    //荷兰国旗问题
    void sortColors(vector<int>& nums) {
        //计数排序
        vector<int> arr(3,0);
        
        //遍历数组记录0、1和2的数量
        for(int i=0;i<nums.size();++i){
            int temp=nums[i];
           switch(temp){
               case 0:
                   ++arr[0];
                   break;
               case 1:
                   ++arr[1];
                   break;
               case 2:
                   ++arr[2];
                   break;
               default:
                   break;
           } 
        }
        
        //重新赋值nums
        for(int i=0;i<arr[0];++i){
            nums[i]=0;
        }
        for(int i=0;i<arr[1];++i){
            nums[arr[0]+i]=1;
        }
        for(int i=0;i<arr[2];++i){
            nums[arr[0]+arr[1]+i]=2;
        }
        
        return;
    }
};

3.2.2 Python

执行结果:44ms,99.92%。
原来Python的切片操作可以这么优雅的循环赋值的…

class Solution:
    def sortColors(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        #荷兰国旗问题
        #遍历数组计算0、1和2的数量
        arr=[0]*3
        for num in nums:
            if num==0:
                arr[0]+=1
            elif num==1:
                arr[1]+=1
            elif num==2:
                arr[2]+=1
        
        #重新赋值nums
        nums[0:arr[0]]=[0]*arr[0]
        nums[arr[0]:arr[1]]=[1]*arr[1]
        nums[arr[0]+arr[1]:]=[2]*arr[2]
        
        return;

4 参考资料

1、快速排序(1) – 荷兰国旗问题

你可能感兴趣的:(Leetcode)