Leetcode 075 颜色分类 Python C++ 史上最详细题解系列(多解法)


每天更新一道python or C++ leetcode题,力求讲解清晰准确,客官们可以点赞或者关注。

题目:

给定一个包含红色、白色和蓝色,一共 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

注意:
不能使用代码库中的排序函数来解决这道题。

示例:

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

进阶:

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

首先给出直观的解决方案,也就是进阶里已经写出来了的。

class Solution:
    def sortColors(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        num0 = 0
        num1 = 0
        num2 = 0
        i = 0
        while i < len(nums):
            if nums[i] == 0:
                num0 += 1
            elif nums[i] == 1:
                num1 += 1
            elif nums[i] == 2:
                num2 += 1
            i += 1
        j = 0
        while j < len(nums):
            if num0 > 0:
                nums[j] = 0
                num0 -= 1
            elif num1 > 0:
                nums[j] = 1
                num1 -= 1
            elif num2 > 0:
                nums[j] = 2
                num2 -= 1
            j += 1
        

这个算法的话可以说基本练过leetcode的人都能写得出。

扫描二维码关注公众号,回复: 2855568 查看本文章

接下来介绍进阶的做法:三向切分。

首先直观地从名字上去理解算法。

这个算法的目的是使的数组分成3个区间,每个区间的元素是相同的,而且这3个区间按大小排序。

好,知道了算法的目的,怎么实现呢?

算法过程:

1.初始化2个指针,一个指向数组的开头-1,一个指向结尾+1

2.zero_end是指的是0的区间的边界所在的index,two_begin是2的区间的边界所开始的index.

3.遇到一个0,我们把那个0和nums[++zero_end]交换,注意这个时候zero_end加1了,同时如果是2,也就是最后一个区间的元素,我们需要与two_begin上所在的元素交换,同时two_begin也减去了1,如果是1,也就是中间区间的元素,则跳过。

class Solution
{
	public:
		// 交换两个数函数 
		void swap(int &a, int &b)
		{
			int temp = a;
			a = b;
			b  = temp;
		} 
		void sortColors(vector<int> &nums)
		{
			int zero_end = -1;
			int two_begin = nums.size();
			int i = 0;
			while (i < two_begin)
			{
				if (nums[i] == 0 && i != ++zero_end)
				{
					swap(nums[i], nums[zero_end]);
				}
				else if (nums[i] == 2 && i != --two_begin)
				{
					swap(nums[i], nums[two_begin]);
				}
				else
					i++;
			} 
		}
};

可能很多人不知道为什么这样能够排序,我这里稍微讲个例子:

[1,...0,...]假设有这么一个数组,第一位是1,第k位出现了第一个0,根据我们的算法,我们会跳过1,然后当我们遇到第一个0的时候,我们会把1和0交换,也就是说变成[0,...1,...],这时候i位变成1了,继续跳过,所以我们的指针能不断前进。

更强更简洁的代码:

接下来要介绍的算法,已经超神了。它不是那么好理解,但它又有三向切分的影子。、

读代码前一定要搞懂:

1.i,j, k 分别指向当前遍历的数组的最后一个0,1,2的位置。

比如,当前已处理好的部分数组是 0 0 0 0 1 1 1 2 2 2 *,那么i,j,k分别指向最后一个0,1,2。m指向*,*号为待处理元素,

 2.++k的结果是k+1

3.如果i,j,k一样,则它们对应的赋值则有可能覆盖对方的赋值。

4.如果没懂,多读几遍

所以我们的算法就是,

遇到0,1,2把i,j,k后面1位的值赋成对应的值即可。

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int i = -1,j = -1,k = -1;//注意i,j,k的含义
        int m;
        for(m = 0; m < nums.size();m++){
            if(nums[m] == 0){
                nums[++k] = 2;
                nums[++j] = 1;
                nums[++i] = 0;
            }
            else if(nums[m] == 1){
                nums[++k] = 2;
                nums[++j] = 1;
            }
            else {
                nums[++k] = 2;
            }
        }
    }
};

总结:

学习了三向切分的2种实现形式,

一种是常规的设置各区间边界的解法:

1.先设置第一个区间的末尾边界,再设置第三个区间的开头边界。

2.遇到第一区间的元素(0),与第一区间末尾边界交换,末尾边界+1;遇到第2区间的元素,跳过;遇到第三区间的元素,与第三区间开头边界交换,并加1。

一种是设置三个指针的三向切分:

i指向第一小元素的最后一个index,j指向第2小元素的最后一个index, k指向第三小元素的最后一个index。

遇到第一小的元素,把i,j,k都往后推一步,并且在赋值。

遇到第2小的元素,由于只会影响2,3区间,于是只用把,j,k往后推一步再赋值。

遇到第3小的元素,只会影响3区间,把k往后推一步赋值。

1区间指第一个区间,也就是最小的区间。

今天总结干货很多,但同时也发现了自己的基础不牢,因为三向切分在《算法》4中讲过,忘干净了。。

猜你喜欢

转载自blog.csdn.net/weixin_41958153/article/details/81603136