algorithm-base/animation-simulation/数据结构和算法/荷兰国旗.md
2021-03-20 16:30:29 +08:00

153 lines
6.3 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

> 如果阅读时发现错误或者动画不可以显示的问题可以添加我微信好友 **[tan45du_one](https://raw.githubusercontent.com/tan45du/tan45du.github.io/master/个人微信.15egrcgqd94w.jpg)** ,备注 github + 题目 + 问题 向我反馈
>
> 感谢支持该仓库会一直维护希望对各位有一丢丢帮助
>
> 另外希望手机阅读的同学可以来我的 <u>[**公众号袁厨的算法小屋**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u> 两个平台同步,想要和题友一起刷题,互相监督的同学,可以在我的小屋点击<u>[**刷题小队**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u>进入。
今天我们一起来看一下可以用快速排序秒杀的经典题或许这些题目大家已经做过不过可以再来一起复习一遍加深印象
阅读这篇文章之前大家要先去看一下我之前写过的快速排序这样才不会对这篇文章一知半解好啦我们一起开整吧
在上篇文章中我们提到了快速排序的优化利用三向切分来优化数组中存在大量重复元素的情况到啦这里各位应该猜到我想写什么了吧
我们今天先来说一下那个非常经典的荷兰国旗问题
> 题目来源https://www.jianshu.com/p/356604b8903f
问题描述
荷兰国旗是由红白蓝3种颜色的条纹拼接而成如下图所示
![荷兰国旗](https://cdn.jsdelivr.net/gh/tan45du/test@master/photo/微信截图_20210305145819.4jrud8f8xny0.png)
假设这样的条纹有多条且各种颜色的数量不一并且随机组成了一个新的图形新的图形可能如下图所示但是绝非只有这一种情况
![荷兰国旗问题](https://cdn.jsdelivr.net/gh/tan45du/test@master/photo/7789414-8baf85cac6228621.62ygbgv09ek0.png)
需求是把这些条纹按照颜色排好红色的在上半部分白色的在中间部分蓝色的在下半部分我们把这类问题称作荷兰国旗问题
我们把荷兰国旗问题用数组的形式表达一下是这样的
给定一个整数数组给定一个值K这个值在原数组中一定存在要求把数组中小于 K 的元素放到数组的左边大于K的元素放到数组的右边等于K的元素放到数组的中间最终返回一个整数数组其中只有两个值分别是等于K的数组部分的左右两个下标值
例如给定数组[2, 3, 1, 9, 7, 6, 1, 4, 5]给定一个值4那么经过处理原数组可能得一种情况是[2, 3, 1, 1, 4, 9, 7, 6, 5]需要注意的是小于4的部分不需要有序大于4的部分也不需要有序返回等于4部分的左右两个下标[4, 4]
这不就是我们之前说过的三向切分吗一模一样
那么 leetcode 有没有相似问题呢我们一起来看下面这道题
**leetcode 75 颜色分类**
题目描述
给定一个包含红色白色和蓝色一共 n 个元素的数组原地对它们进行排序使得相同颜色的元素相邻并按照红色白色蓝色顺序排列
此题中我们使用整数 0 1 2 分别表示红色白色和蓝色
示例 1
> 输入nums = [2,0,2,1,1,0]
> 输出[0,0,1,1,2,2]
示例 2
> 输入nums = [2,0,1]
> 输出[0,1,2]
示例 3
> 输入nums = [0]
> 输出[0]
示例 4
> 输入nums = [1]
> 输出[1]
**做题思路**
这个题目我们使用 Arrays.sort() 解决哈哈但是那样太无聊啦题目含义就是让我们将所有的 0 放在前面2放在后面1 放在中间是不是和我们上面说的荷兰国旗问题一样我们仅仅将 1 做为 pivot
下面我们直接看代码吧和三向切分基本一致
```java
class Solution {
public void sortColors(int[] nums) {
int len = nums.length;
int left = 0;
//这里和三向切分不完全一致
int i = left;
int right = len-1;
while (i <= right) {
if (nums[i] == 2) {
swap(nums,i,right--);
} else if (nums[i] == 0) {
swap(nums,i++,left++);
} else {
i++;
}
}
}
public void swap (int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
```
另外我们看这段代码有什么问题呢那就是我们即使完全符合时仍会交换元素这样会大大降低我们的效率
例如[0,0,0,1,1,1,2,2,2]
此时我们完全符合情况不需要交换元素但是按照我们上面的代码0,2 的每个元素会和自己进行交换所以这里我们可以根据 i left 的值是否相等来决定是否需要交换大家可以自己写一下
下面我们看一下另外一种写法
这个题目的关键点就是当我们 nums[i] nums[right] 交换后我们的 nums[right] 此时指向的元素是符合要求的但是我们 nums[i] 指向的元素不一定符合要求所以我们需要继续判断
![细节地方](https://cdn.jsdelivr.net/gh/tan45du/test@master/photo/微信截图_20210305153911.28capmzljy80.png)
我们 2 0 交换后此时 i 指向 0 0 应放在头部所以不符合情况所以 0 1 仍需要交换下面我们来看一下动画来加深理解吧
![](https://img-blog.csdnimg.cn/20210318093047325.gif#pic_center)
另一种代码表示
```java
class Solution {
public void sortColors(int[] nums) {
int left = 0;
int len = nums.length;
int right = len - 1;
for (int i = 0; i <= right; ++i) {
if (nums[i] == 0) {
swap(nums,i,left);
left++;
}
if (nums[i] == 2) {
swap(nums,i,right);
right--;
//如果不等于 1 则需要继续判断,所以不移动 i 指针i--
if (nums[i] != 1) {
i--;
}
}
}
}
public void swap (int[] nums,int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
```
好啦这个问题到这就结束啦是不是很简单啊我们明天见