漫画:什么是归并排序?

简介: 举个例子,有A、B、C、D、E、F、G、H一共8个武术家参考参加比武大会。第一轮,两两一组,有4名选手胜出(四分之一决赛)第二轮,两两一组,有两名选手胜出(半决赛)第三轮,仅剩的两人一组,冠军胜出(总决赛)


640.jpg640.jpg


—————  第二天  —————



640.jpg640.jpg640.jpg640.jpg640.jpg640.jpg640.jpg

————————————

640.jpg640.jpg640.jpg640.jpg640.jpg640.jpg640.jpg640.jpg640.jpg



举个例子,有A、B、C、D、E、F、G、H一共8个武术家参考参加比武大会。

第一轮,两两一组,有4名选手胜出(四分之一决赛)

第二轮,两两一组,有两名选手胜出(半决赛)

第三轮,仅剩的两人一组,冠军胜出(总决赛)


640.jpg640.jpg

归并排序和擂台赛,有什么相同和不同之处呢?让我们以下面这个数组来举例说明:

640.png

归并排序就像是组织一场元素之间的“比武大会”,这场比武大会分成


两个阶段:



1.分组


假设集合一共有n个元素,算法将会对集合进行逐层的折半分组。

第一层分成两个大组,每组n/2个元素;

第二层分成4个小组,每组n/4个元素;

第三层分成8个更小的组,每组n/8个元素;

......

一直到每组只有一个元素为止。

这样一来,整个数组就分成了一个个小小的“擂台”。


image.gif

640.png


2.归并


既然分了组,接下来就要开始“比武”了。

归并排序和擂台赛有一个很大的不同,就是擂台赛只需要决定谁是老大,而并不关心谁做老二和老三;归并排序的要求复杂一些,需要确定每一个元素的排列位置。

因此,当每个小组内部比较出先后顺序以后,小组之间会展开进一步的比较和排序,合并成一个大组;大组之间继续比较和排序,再合并成更大的组......最终,所有元素合并成了一个有序的集合。


640.png

这个比较与合并的过程叫做归并,对应英文单词merge,这正是归并排序名字的由来。


image.gif640.jpg640.jpg

image.gif


归并操作需要哪三个步骤呢?我们以两个长度为4的集合为例:

image.gif640.png

第一步,创建一个额外大集合用于存储归并结果,长度是两个小集合之和。(p1,p2,p是三个辅助指针,用于记录当前操作的位置)


image.gif640.png

第二步,从左到右逐一比较两个小集合中的元素,把较小的元素优先放入大集合。

由于1<2,所以把元素1放入大集合,p1和p各右移一位:


image.gif640.png

由于2<3,所以把元素2放入大集合,p2和p各右移一位:


image.gif640.png


由于3<7,所以把元素3放入大集合,p1和p各右移一位:

640.pngimage.gif

由于5<7,所以把元素5放入大集合,p1和p各右移一位:


image.gif640.png

由于6<7,所以把元素6放入大集合,p1和p各右移一位:


640.pngimage.gif

此时左侧的小集合已经没有元素可用了。

第三步,从另一个还有剩余元素的集合中,把剩余元素按顺序复制到大集合尾部。

640.png

这样一来,两个有序的小集合就归并成了一个有序的大集合。

image.gif

640.jpg640.jpg

static
public
void
 mergeSort
(
int
[]
 array
,
int
 start
,
int
end
){
if
(
start 
<
end
){
//折半成两个小集合,分别进行递归
int
 mid
=(
start
+
end
)/
2
;
        mergeSort
(
array
,
 start
,
 mid
);
        mergeSort
(
array
,
 mid
+
1
,
end
);
//把两个有序小集合,归并成一个大集合
        merge
(
array
,
 start
,
 mid
,
end
);
}
}
static
private
void
 merge
(
int
[]
 array
,
int
 start
,
int
 mid
,
int
end
){
//开辟额外大集合,设置指针
int
[]
 tempArray 
=
new
int
[
end
-
start
+
1
];
int
 p1
=
start
,
 p2
=
mid
+
1
,
 p
=
0
;
//比较两个小集合的元素,依次放入大集合
while
(
p1
<=
mid 
&&
 p2
<=
end
){
if
(
array
[
p1
]<=
array
[
p2
])
            tempArray
[
p
++]=
array
[
p1
++];
else
            tempArray
[
p
++]=
array
[
p2
++];
}
//左侧小集合还有剩余,依次放入大集合尾部
while
(
p1
<=
mid
)
        tempArray
[
p
++]=
array
[
p1
++];
//右侧小集合还有剩余,依次放入大集合尾部
while
(
p2
<=
end
)
        tempArray
[
p
++]=
array
[
p2
++];
//把大集合的元素复制回原数组
for
(
int
 i
=
0
;
 i
<
tempArray
.
length
;
 i
++)
        array
[
i
+
start
]=
tempArray
[
i
];
}
public
static
void
 main
(
String
[]
 args
)
{
int
[]
 array 
=
{
5
,
8
,
6
,
3
,
9
,
2
,
1
,
7
};
    mergeSort
(
array
,
0
,
 array
.
length
-
1
);
System
.
out
.
println
(
Arrays
.
toString
(
array
));
}

640.jpg640.jpg640.png640.jpg640.jpg640.jpg640.jpg640.jpg640.jpg640.jpg640.png640.jpg640.jpg

相关文章
Need to install docker-compose(1.18.0+) by yourself first and run this script again.
Need to install docker-compose(1.18.0+) by yourself first and run this script again.
635 0
|
前端开发 API 开发者
harmonyOS基础- 快速弄懂HarmonyOS ArkTs基础组件、布局容器(前端视角篇)
本文由黑臂麒麟(6年前端经验)撰写,介绍ArkTS开发中的常用基础组件与布局组件。基础组件包括Text、Image、Button等,支持样式设置如字体颜色、大小和加粗等,并可通过Resource资源引用统一管理样式。布局组件涵盖Column、Row、List、Grid和Tabs等,支持灵活的主轴与交叉轴对齐方式、分割线设置及滚动事件监听。同时,Tabs组件可实现自定义样式与页签切换功能。内容结合代码示例,适合初学者快速上手ArkTS开发。参考华为开发者联盟官网基础课程。
1217 75
harmonyOS基础- 快速弄懂HarmonyOS ArkTs基础组件、布局容器(前端视角篇)
|
Oracle Java 关系型数据库
入职必会-开发环境搭建41-Linux软件安装-安装JDK
本文介绍了在Linux系统中下载和安装JDK
663 3
入职必会-开发环境搭建41-Linux软件安装-安装JDK
|
存储 缓存 NoSQL
redis缓存优化
采用获取一次缓存,如果为空的情况,获取分布式锁,让一个线程去重建缓存,另外的线程未获取到锁的情况,休眠短时间,然后再自旋获取缓存。
309 0
|
NoSQL Redis
基于若依ruoyi-nbcio增加flowable流程待办消息的提醒,并提供右上角的红字数字提醒(二)
基于若依ruoyi-nbcio增加flowable流程待办消息的提醒,并提供右上角的红字数字提醒(二)
312 0
|
JavaScript 前端开发 开发者
Vue学习之--------深入理解Vuex、原理详解、实战应用(2022/9/1)
这篇文章详细介绍了Vuex的基本概念、使用场景、安装配置、基本用法、实际应用案例以及注意事项,通过一个数字累加器的实战示例,帮助开发者深入理解Vuex的原理和应用。
|
机器学习/深度学习 人工智能 分布式计算
Java中的机器学习模型集成与训练
Java中的机器学习模型集成与训练
|
SQL 存储 关系型数据库
Flink(十四)【Flink SQL(中)查询】(2)
Flink(十四)【Flink SQL(中)查询】