DS八大排序之冒泡排序和快速排序

前言

前两期我们已经对"插入排序"(直接插入排序和希尔排序) 和 "选择排序"(直接选择排序和堆排序)进行了详细的介绍~!这一期我们再来详细介绍一组排序 :"交换排序"即耳熟能详的冒泡排序和赫赫有名的快速排序~!

本期内容介绍

冒泡排序

快速排序(Hoare、挖坑、前后指针、非递归)

交换排序的基本思想

对待排序的序列,进行元素的两两比较,如果满足交换条件交换。即将元素逐步换到合适的位置~!

冒泡排序

从前往后,逐一比较相邻元素,前面的大于或小于后面的则进行交换,每一轮将当前轮最大或最小的元素冒泡到当前论的最后。重复上述过程最后就是有序的~!

OK,还是画个图理解一下:

OK,这就是冒泡排序的过程,我们还是先来写单趟,再来改造整体:

单趟

	//注意,前一个和后一个比,j最大只能走到n-2(倒数第二个),j+1只能走到n-1(倒数第一个)for (int j = 0; j < n - 1; j++){if (a[j] > a[j + 1]){Swap(&a[j], &a[j + 1]);}}

整体

整体的话,控制一下每一趟的交换个数即可~!

//冒泡排序
void BubbleSort(int* a, int n)
{for (int i = 0; i < n - 1; i++)//n-1是最后一个只能放在第一个即可以不对他排{//注意,前一个和后一个比,j最大只能走到n-2(倒数第二个),j+1只能走到n-1(倒数第一个)for (int j = 0; j < n - 1 - i; j++)//每一趟确定当前趟的最大之到当前趟的最后{								//每一趟确定出一个即每一趟少排i个if (a[j] > a[j + 1]){Swap(&a[j], &a[j + 1]);}}}
}

OK, 测试一下!看结果:

没问题!但有一种情况就是上述画图的那种例子,已经有序了但不知道还是在两两比较。这其实是很没有必要的~!请我们可以优化一下!

优化思路:在每一趟开始之前进行一个有序的标记,当一趟结束后判断该标记,如果有序直接不用再往后排了,否则继续进行排序~!

//冒泡排序
void BubbleSort(int* a, int n)
{for (int i = 0; i < n - 1; i++)//n-1是最后一个只能放在第一个即可以不对他排{int flag = 1;//假设该趟有序//注意,前一个和后一个比,j最大只能走到n-2(倒数第二个),j+1只能走到n-1(倒数第一个)for (int j = 0; j < n - 1 - i; j++)//每一趟确定当前趟的最大之到当前趟的最后{								//每一趟确定出一个即每一趟少排i个if (a[j] > a[j + 1]){Swap(&a[j], &a[j + 1]);flag = 0;//交换了说明是该趟是无序的}}if (flag == 1)//说明已经有序了没有必要再冒泡了{break;}}
}

冒泡这是一种写法,其实还有很多。这里再来一个:这里前面与后面比较!

单趟

		//后一个和前一个比较,j最大走到倒数第一个for (int j = 1; j < n - i; j++){if (a[j - 1] > a[j])//后面与前一个比较{Swap(&a[j - 1], &a[j]);flag = 0;}}

整体

//冒泡排序
void BubbleSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){int flag = 1;//后一个和前一个比较,j最大走到倒数第一个for (int j = 1; j < n - i; j++){if (a[j - 1] > a[j])//后面与前一个比较{Swap(&a[j - 1], &a[j]);flag = 0;}}if (flag == 1)//已经有序不要再去冒泡了{break;}}
}

测试一下:

一点问题没有~!

总结:冒泡虽然简单,但一定要注意边界点的控制~!

复杂度分析

时间复杂度:O(N^2)

每一趟遍历选出一个最值即每一趟都比前一趟少比较一次,所以他应该是第一趟比较n-1次,第二趟比较n-2次...1很明显这是等差数列求和,最后的时间复杂度为O(N^2)

空间复杂度:O(1)

快速排序

一个序列中先随意选出一个元素,该元素称为基准比基准移动到基准的右边比基准移动到基准的左边,这样基准值就到了他该到的位置。然后对基准值的左右区间分别进行上述相同的操作

相信看到这里应该想到快排用的是递归,是的!但我们也会实现非递归版本~!

快排最核心的就是他选基准的那个单趟!这里的版本我会的有三个:Hoare、挖坑法、前后指针。下面一个一个的来!

Hoare

择最左端或最右端作为基准点,使用两个指针从序列两端向中间扫描右指针找到比基准的值,左指针找到比基准的值,然后进行交换。重复这个过程直到左右指针相遇,相遇的位置是基准的最终位置。(为什么相遇的位置就是最终的位置呢?后面会解释!)

OK,还是画个图理解一下:

OK,上代码:

//Hoare
int PartSort1(int* a, int left, int right)
{int keyi = left;//选最左端的为基准while (left < right){//右指针找小while (left < right && a[right] >= a[keyi])right--;//左指针找大while (left < right && a[left] <= a[keyi])left++;//找到了,交换Swap(&a[right], &a[left]);}//当相遇时left或right与基准交换Swap(&a[left], &a[keyi]);return left;//返回基准下标
}

解释:

1、这里找大或找小时可能在极端情况下找不到,从而导致越界。所以得判断让其不要越界~!

2、为什么在左右指针相遇时就是基准的最终位置?左右指针相交有两种情况,左找右和右找左)

左找右:因为是右先找小,所以当他们相交时,一定是小于基准的。

右找左:因为前一轮已经交换过,所以当前左一定是小于基准的的。 

这就保证了,当左右相交时的位置与基准的位置交换后基准的位置是最终位置!

OK,单趟写好了就可以用相同的方式去处理左右区间了~!而每个区间的处理和上述的处理方式一样,所以使用递归就很方便~!

我们以前在函数那一期介绍递归的时候说过,递归必须有结束条件~!这个的结束条件是啥吧呢?

其实很简单,只需要注意每次递归的那个区间合法即可!即左区间 < 右区间(相等只有一个元素也不需要排了)

整体

//快速排序
void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int key = PartSort1(a, begin, end);//[begin, key-1] key [key+1, end]QuickSort(a, begin, key - 1);QuickSort(a, key + 1, end);
}

测试一把:

其实介绍到这里,你有没有感觉到。快排很像二叉树的前序遍历~!

OK,我们再来看看,Hoare的优化版(国人优化的)挖坑法!

挖坑法

和Hoare的很相似。左右两指针向中间扫描右找小,左找大最左端或最右端为基准基准位置空出来了形成了坑右指针(左指针)先走,找到小(大)的了,填到左(右)边里面。自身了坑,(右)边找大(小),填到右(左)边的。如此循环,直到相交!然后把基准与左右指针的任意交换即可~!

OK,还是画个图:

OK,上代码:

//挖坑法
int PartSort2(int* a, int left, int right)
{int keyi = a[left];//选左端为基准,左端就是坑位while (left < right){//右指针找小while (left < right && a[right] >= keyi)right--;a[left] = a[right];//找到了,填到左坑,自身成了新坑位//左指针找大while (left < right && a[left] <= keyi)left++;a[right] = a[left];//找到了,填到右坑,自身成了新坑位}//左右指针相遇,交换基准与左右指针的任意Swap(&a[left], &keyi);return left;//返回基准的下标
}

整体

//快速排序
void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int key = PartSort2(a, begin, end);//[begin, key-1] key [key+1, end]QuickSort(a, begin, key - 1);QuickSort(a, key + 1, end);
}

测试一下:

OK,没有问题!下面我们在来看一个版本~前后指针。

前后指针

选左端为基准,前指针在第一个位置,后指针的第二个位置。当前指针遇到小于基准值时,当前位置的值与后指针的下一个位置的值进行交换。直到前指针到区间结束,此时后指针与基准交换,后指针的位置就是最终基准的位置!

OK,还是来画个图:

OK,上代码:

//前后指针
int PartSort3(int* a, int left, int right)
{int prev = left;//后(慢)指针int keyi = left;//基准int cur = left + 1;//快指针while (cur <= right)//闭区间所以是<={if (a[cur] < a[keyi])//快指针如果找到小了{Swap(&a[++prev], &a[cur]);//与prev的下一个位置交换}++cur;}Swap(&a[prev], &a[keyi]);//最后交换基准位置与prev位置的值return prev;//返回基准的位置
}

这里其实可以小小的优化一下:和上面画图情况的一样,假设prev和cur是同一个位置时,是不是根本就不用交换啊~!OK,我们可以控制一下

//前后指针
int PartSort3(int* a, int left, int right)
{int prev = left;//后(慢)指针int keyi = left;//基准int cur = left + 1;//快指针while (cur <= right)//闭区间所以是<={if (a[cur] < a[keyi] && ++prev != cur)//快指针如果找到小了并且prev的位置和cur不同{Swap(&a[prev], &a[cur]);//与prev的下一个位置交换}++cur;}Swap(&a[prev], &a[keyi]);//最后交换基准位置与prev位置的值return prev;//返回基准的位置
}

整体

//快速排序
void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int key = PartSort3(a, begin, end);//[begin, key-1] key [key+1, end]QuickSort(a, begin, key - 1);QuickSort(a, key + 1, end);
}

测试一下:

OK,没有问题~!

以上就是三个版本快排了,他们都是递归版本的。以前介绍的递归时说过递归有个致命的缺陷是:如果递归的太深会栈溢出(每一次调用都要建立函数栈帧,一般的情况下栈区的大小时7M左右)~!为了解决栈溢出的问题我们采用非递归即迭代的方式来解决这个问题~!下面我们来实现一下~!

非递归版本

非递归版本,其实时利用栈来模拟递归的这个过程的~!但C语言没有栈这种数据结构...得手搓,我们就把以前的在栈和队列那一期的那个拿过来。

实现思路:模拟递归的方式!先让数组的 0 和 size-1 左右端点的下标入栈,当栈不为空时分别取栈顶元素,赋值给左右指针。然后调用任意一个版本的单趟获得基准,然后基准左右的两个区间入栈继续上述操作!直到栈为空就排序结束了~!

OK,画个图:

OK,上代码:

//快速排序 非递归版
void QuickSort(int* a, int begin, int end)
{ST* s = NULL;//先把左右区间入栈Push(&s, end);Push(&s, begin);//栈不为空时,出left和rightwhile (!STEmpty(s)){//获取左端点int left = STTop(s);Pop(&s);		//获取右端点int right = STTop(s);Pop(&s);//获取该区间的基准int keyi = PartSort3(a, left, right);//右子区间入栈if (keyi + 1 < right){Push(&s, right);Push(&s, keyi + 1);;}//左子区间入栈if (left < keyi - 1){Push(&s, keyi-1);Push(&s, left);}}STDestory(&s);
}

OK,测试一下:

分析以及优化

我们前面说过快速排序和二叉树的前序遍历很象,然而上述的版本的单趟和一些情况下其实可以做一下一些小优化~!第一当我们的待排序的序列是已经有序的时!我们快速排序的时间复杂度时很高的,接近O(n^2)如下图1分析,避免这种情况我们采用三数取中的方式来解决。第二递归的最后几层是整个递归的80%左右,而递归要建立函数栈帧空间消耗比较大,我们可以在区间小的时候,换成直接插排提高效率~!

图1:快排最差情况(已经有序)

解决已经有序的序列排序效率低的问题 --->三数取中

三数取中:当前待排的序列的最左端、最右端、最中间。三个值中取中间大的那一个~!这样就不怕有序的情况效率低了,而且是越有序越效率高~!

代码实现:

//三数取中
int GetMid(int* a, int left, int right)
{int mid = left + (right - left) / 2;if (a[left] > a[right]){if (a[mid] < a[right]){return right;}else if (a[mid] > a[left]){return left;}elsereturn mid;}else{//left < rightif (a[mid] > a[right]){return right;}else if (a[mid] < a[left]){return left;}elsereturn mid;}
}

当区间小的时候,我们可以采用指直接插排来优化。

原因:在序列很大时当区间小的时候就说明此小区间已经接近有序了,而接近有序的区间直接插入排序的效率很高的。

代码实现:

void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;if (end - begin + 1 > 10){int key = PartSort1(a, begin, end);//[begin, key-1] key [key+1, end]QuickSort(a, begin, key - 1);QuickSort(a, key + 1, end);}else{InsertSort(a, end - begin + 1);//区间小于10个待排序的元素后进行直接插排}
}

复杂度分析

时间复杂度:O(N*logN)

快排可以看做一棵二叉树,一共有N个节点。每一层确定2^(i-1)(i从1开始)个待排元素的最终位置,总共的确定待排的层数是:2^x = N ---> x = logN,而每一次确定一个元素的位置又是遍历一遍待排的序列即O(N)所以总共合计O(N*logN)

注意:加了三数取中,几乎不可能再出现O(N^2)了

空间复杂度:O(logN)

因为递归是要开销栈帧的,我们前面在复杂度的那一期介绍过,空间可以重复利用而时间不可重复利用。所以这里至多递归到他的深度即h = logN,所以他的空间复杂度是O(logN)

注意:非递归的空间复杂度任然是log(N)原因是他的空间消耗虽然不在栈了,但他利用栈的数据结构转移到了堆上,还是会消耗空间的~!!

优化后的快排源码:

//三数取中
int GetMid(int* a, int left, int right)
{int mid = left + (right - left) / 2;if (a[left] > a[right]){if (a[mid] < a[right]){return right;}else if (a[mid] > a[left]){return left;}elsereturn mid;}else{//left < rightif (a[mid] > a[right]){return right;}else if (a[mid] < a[left]){return left;}elsereturn mid;}
}Hoare 
O(N)
int PartSort1(int* a, int left, int right)
{int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);int keyi = left;while (left < right){while (left < right && a[right] >= a[keyi])right--;while (left < right && a[left] <= a[keyi])left++;Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);return left;
}挖坑法
O(N)
int PartSort2(int* a, int left, int right)
{int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);int keyi = a[left];while (left < right){while (left < right && a[right] >= keyi)right--;a[left] = a[right];while (left < right && a[left] <= keyi)left++;a[right] = a[left];}a[left] = keyi;return left;
}前后指针
O(N)
int PartSort3(int* a, int left, int right)
{int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);int prev = left;//后(慢)指针int keyi = left;//基准int cur = left + 1;//快指针while (cur <= right)//闭区间所以是<={//if (a[cur] < a[keyi])//快指针如果找到小了//{//	Swap(&a[++prev], &a[cur]);//与prev的下一个位置交换//}if (a[cur] < a[keyi] && ++prev != cur)//快指针如果找到小了并且prev的位置和cur不同{Swap(&a[prev], &a[cur]);//与prev的下一个位置交换}++cur;}Swap(&a[prev], &a[keyi]);//最后交换基准位置与prev位置的值return prev;//返回基准的位置
}//快速排序
//O(N*logN)
void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;if (end - begin + 1 > 10){int key = PartSort1(a, begin, end);//[begin, key-1] key [key+1, end]QuickSort(a, begin, key - 1);QuickSort(a, key + 1, end);}else{InsertSort(a, end - begin + 1);//区间小于10个待排序的元素后进行直接插排}
}//快速排序 非递归版
//void QuickSort(int* a, int begin, int end)
//{
//	ST* s = NULL;
//	//先把左右区间入栈
//	Push(&s, end);
//	Push(&s, begin);
//
//	//栈不为空时,出left和right
//	while (!STEmpty(s))
//	{
//		//获取左端点
//		int left = STTop(s);
//		Pop(&s);		
//		//获取右端点
//		int right = STTop(s);
//		Pop(&s);
//		//获取该区间的基准
//		int keyi = PartSort3(a, left, right);
//		//右子区间入栈
//		if (keyi + 1 < right)
//		{
//			Push(&s, right);
//			Push(&s, keyi + 1);;
//		}
//		//左子区间入栈
//		if (left < keyi - 1)
//		{
//			Push(&s, keyi-1);
//			Push(&s, left);
//		}
//	}
//
//	STDestory(&s);
//}

OK,好兄弟我们本期分享就到这里,我们下一期的归并排序再见~!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/218676.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

等等Domino 14.0FP1

大家好&#xff0c;才是真的好。 节奏确实太快了&#xff0c;有时候我深感我也追不上。 以前Notes Domino是三年磨一剑&#xff0c;也就说每三年才发一个大版本&#xff0c;从2019年开始&#xff0c;进行了高频提速&#xff0c;居然一年一个大版本&#xff01; 周末&#xf…

树莓派(Raspberry Pi)4B密码忘记了,怎么办?

树莓派长时间不用&#xff0c;导致密码忘记了&#xff0c;这可咋整&#xff1f; 第1步&#xff1a;取出SD卡 将树莓派关机&#xff0c;移除sd卡&#xff0c;使用读卡器&#xff0c;插入到你的电脑。 第2步&#xff1a;编辑 cmdline.txt 在PC上打开SD卡根目录&#xff0c;启动…

基于C/C++的rapidxml加载xml大文件 - 上部分翻译

RAPIDXML手册 版本 1.13 版权所有 &#xff08;C&#xff09; 2006&#xff0c; 2009 Marcin Kalicinski有关许可证信息&#xff0c;请参阅随附的文件许可证 .txt。 目录 1. 什么是 RapidXml&#xff1f; 1.1 依赖性和兼容性1.2 字符类型和编码1.3 错误处理1.4 内存分配1.5 …

C++相关闲碎记录(16)

1、正则表达式 &#xff08;1&#xff09;regex的匹配和查找接口 #include <regex> #include <iostream> using namespace std;void out (bool b) {cout << ( b ? "found" : "not found") << endl; }int main() {// find XML/H…

[笔记] wsl 下使用 qemu/grub 模拟系统启动(单分区)

背景 最近在学习操作系统&#xff0c;需要从零开始搭建系统&#xff0c;由于教程中给的虚拟机搭建的方式感觉还是过于重量级&#xff0c;因此研究了一下通过 qemu 模拟器&#xff0c;配合 grub 完成启动系统的搭建。 qemu 介绍 qemu 是一款十分优秀的系统模拟器&#xff0c;…

Qt之自定义QToolTip,去掉显示动画和隐藏延时

一.效果 先来看看Qt原生QToolTip的缺点: 1.当提示内容无变化时,弹窗无法移动。只能先传个空字符串强制弹窗隐藏,然后在新位置再传个字符串。 If the text is the same as the currently shown tooltip, the tip will not move. You can force moving by first hiding the t…

【Hadoop_06】MapReduce的概述与wc案例

1、MapReduce概述1.1 MapReduce定义1.2 MapReduce优点1.3 MapReduce缺点1.4 MapReduce核心思想1.5 MapReduce进程1.6 常用数据序列化类型1.7 源码与MapReduce编程规范 2、WordCount案例实操2.1 本地测试2.2 提交到集群测试 1、MapReduce概述 1.1 MapReduce定义 MapReduce是一…

HPM6750系列--第九篇 GPIO详解(基本操作)

一、目的 在之前的博文中我们主要介绍了不同系统不同开发编译调试环境的配置和操作&#xff08;命令行方式、Visual Studio Code、Segger Embedded Studio for RISC-V&#xff09;&#xff0c;以帮助大家准备好学习环境为目的&#xff0c;但是未涉及到芯片本身以及外设的讲解。…

苹果计划将全球1/4的IPhone产能转移至印度

KlipC报道&#xff1a;据相关人士报道&#xff0c;苹果希望在未来2到3年内每年在印度生产超过5000万部iphone&#xff0c;要是该计划得以实现&#xff0c;印度将占领全球iPhone产量的四分之一。 KlipC的分析师Alex Su表示&#xff1a;“此次iPhone15推出是苹果印度制造计划的一…

设计模式详解---策略模式

1. 策略模式简介 策略模式&#xff08;Strategy Pattern&#xff09;是一种行为型设计模式&#xff0c;用于在运行时根据不同的情境选择不同的算法或策略。该模式将算法封装成独立的类&#xff0c;使得它们可以相互替换&#xff0c;而且可以独立于客户端使用它们的方式。 1.1.…

m_map导入本地地形数据

m_map绘制地形图时&#xff0c;虽然自带有1的地形图以及从NOAA下载的1分的地形图&#xff08;详见&#xff1a;Matlab下地形图绘图包m_map安装与使用&#xff09;&#xff0c;但有时需要对地形图分辨率的要求更高&#xff0c;便无法满足。 此时&#xff0c;需要导入本地地形数…

二蛋赠书十一期:《TypeScript入门与区块链项目实战》

前言 大家好&#xff01;我是二蛋&#xff0c;一个热爱技术、乐于分享的工程师。在过去的几年里&#xff0c;我一直通过各种渠道与大家分享技术知识和经验。我深知&#xff0c;每一位技术人员都对自己的技能提升和职业发展有着热切的期待。因此&#xff0c;我非常感激大家一直…

Toyota Programming Contest 2023#8(AtCoder Beginner Contest 333)

A - Three Threes 题目大意&#xff1a;给你一个整数n&#xff0c;将这个数n输出n次。 呃呃 B - Pentagon 题目大意&#xff1a;给你一个正五边形ABCDE&#xff0c;给你任意两条边&#xff0c;判断是否相等 主要问题要判断一下内边&#xff1a;AD&#xff0c;AC&#xff0c;…

MIT6.5840-2023-Lab2C: Raft-Persistence

前置知识 见上一篇 Lab2A。 实验内容 实现 RAFT&#xff0c;分为四个 part&#xff1a;leader election、log、persistence、log compaction。 实验环境 OS&#xff1a;WSL-Ubuntu-18.04 golang&#xff1a;go1.17.6 linux/amd64 Part 2C: persistence 大部分的bug都与这…

KubeKey 离线部署 KubeSphere v3.4.1 和 K8s v1.26 实战指南

作者&#xff1a;运维有术 前言 知识点 定级&#xff1a;入门级了解清单 (manifest) 和制品 (artifact) 的概念掌握 manifest 清单的编写方法根据 manifest 清单制作 artifactKubeKey 离线集群配置文件编写KubeKey 离线部署 HarborKubeKey 离线部署 KubeSphere 和 K8sKubeKey…

C++初阶-list类的模拟实现

list类的模拟实现 一、基本框架1.1 节点类1.2 迭代器类1.3 list类 二、构造函数和析构函数2.1 构造函数2.2 析构函数 三、operator的重载和拷贝构造3.1 operator的重载3.2 拷贝构造 四、迭代器的实现4.1 迭代器类中的各种操作4.1 list类中的迭代器 五、list的增容和删除5.1 尾插…

javacv的视频截图功能

之前做了一个资源库的小项目&#xff0c;因为上传资源文件包含视频等附件&#xff0c;所以就需要时用到这个功能。通过对视频截图&#xff0c;然后作为封面缩略图&#xff0c;达到美观效果。 首先呢&#xff0c;需要准备相关的jar包&#xff0c;之前我用的是低版本的1.4.2&…

Tomcat-安装部署(源码包安装)

一、简介 Tomcat 是由 Apache 开发的一个 Servlet 容器&#xff0c;实现了对 Servlet 和 JSP 的支持&#xff0c;并提供了作为Web服务器的一些特有功能&#xff0c;如Tomcat管理和控制平台、安全域管理和Tomcat阀等。 简单来说&#xff0c;Tomcat是一个WEB应用程序的托管平台…

基于Nexus搭建Maven私服基础入门

什么是Nexus&#xff1f;它有什么优势&#xff1f; 要了解为什么需要nexus的存在&#xff0c;我们不妨从以下几个问题来简单了解一下: 为什么需要搭建私服&#xff1f;如果没有私服会出现什么问题&#xff1f; 对于企业开发而言&#xff0c;如果没有私服&#xff0c;我们所有…

十九)Stable Diffusion使用教程:ai室内设计案例

今天我们聊聊如何通过SD进行室内设计装修。 方式一:controlnet的seg模型 基础起手式: 选择常用算法,抽卡: 抽到喜欢的图片之后,拖到controlnet里: 选择seg的ade20k预处理器,点击爆炸按钮,得到seg语义分割图,下载下来: 根据语义分割表里的颜色值,到PS里进行修改: 语…