seegongsik
算法

归并排序 怎么工作

上次我们通过比较相邻的两个、把大的往后送来排队。这样能排好,可队伍一长就太慢了,因为卡片越多,比较次数就多得吓人。有没有更聪明的办法?有,就是把队伍一分为二。

01

一格一格太慢

上次的方法好在简单:比较相邻的两个,把大的往后送就行。
可是队伍一长,要比较的就太多了。
卡片翻一倍,比较次数就涨到四倍。四张很快,三十二张就遥遥无期了。

队伍长度4
一格一格的比较次数6
队伍每翻一倍,比较就跳到四倍。一格一格地比的排序,队伍一长就很快吃力。

把队伍翻一倍试试。长度翻倍 → 比较次数涨到四倍。

柱子吓人地往上蹿。
队伍越长,慢得越厉害。
问题出在把整支队伍当成一整块,从头比到尾。
那么,把这一整块拆开会怎样?

02

一分为二

不要把长队伍整个拿来处理,而是一分为二。
把这一半再分成两半,再分,再分,最后就只剩下一格一格的。
而一格本身就算排好了,因为只有一个,没有可比的。

5
2
8
1
9
4
7
3
束的数量1
点一下,把一束不停地对半分。越分,束越小、越多。

把一组不断地一分为二。8 → 4+4 → 2+2+2+2 → 一格一格。

光拆开就完了吗?
没有。拆开的还得重新合起来才成队伍:把单格两两配对合并,再把合好的继续合并。
而把两组排好的合并,其实意外地简单。

03

合并两组排好的

把两组排好的合成一队,诀窍是只看最前面。
比较两组的第一格,把小的那个放进结果里。那个位置由下一格补上,再比较两组的第一格。
就像拉拉链一样,一格一格地咬合起来。

左束(已排好)
2
4
7
右束(已排好)
1
5
8
合并的队
—
只比两束的最前面,把较小的落进结果里。每一束都已排好,所以只看最前面就行。

比较两组最前面的,把小的放下去。像拉链一样一格一格合并。

是不是很神奇?比较整个乱糟糟的队伍时很慢,可合并两组各自排好的,只看最前面就行,所以很快。
拆开以后有了排好的小块,再把这些小块一层层合起来,不知不觉就成了完整的队伍。

04

为什么快:层数

为什么这样更快?
因为是一分为二,拆的层数并不多。队伍翻一倍,层数只多一层。
合并一层的代价大约是队伍长度,所以总代价就是层数乘以队伍长度。
这比一格一格比较(长度乘长度)增长得慢得多。

队伍长度8
分开的层数3
每层的花费8
合并花费(层数 x 每层)24
一格一格的话28
队伍翻一倍,层只多一个。所以层数乘以每层花费,比一格一格(长度乘长度)增长得慢得多。

展开各层,改变队伍长度试试。层数 x 一层 = 比一格一格增长得慢。

准确的公式以后再学。
现在只要带走一个感觉:一分为二,层数增长得慢,每一层又合并得轻松,所以整体就快了。
拆开再合并,这一招就让速度大大改观。

05

总结

串成一条线,就是这样。
一格一格比较的排序,队伍越长越慢。
所以把队伍一分为二,一直拆到单格,单格就是已经排好的小块。
把排好的两组像拉链一样合并,只要合并层数那么多次,整支队伍就能快得多地排好。
拆开再合并,这就是聪明排序的核心。

依次点这四步,跟着走一圈。拆开 → 单格 → 合并 → 只合并层数那么多次 = 快。

这里我们总是从中间平平稳稳地一分为二。
不过还有个“表亲”,选分界点的方法稍有不同:不选中间,而是选某一格作为基准,把其余的分成比它小和比它大的两部分。
这个基准怎么选,就留到以后再讲。
今天只要记住:一分为二再合并,就会变快。

一句话总结一格一格比较相邻两个的排序,队伍越长比较次数越是暴增,所以很慢。更聪明的办法是把队伍一分为二,分别排好,再把排好的两半像拉链一样合并。一直拆下去只剩单格,而单格本身就算排好了;合并时只比较两边最前面的,所以很快。一分为二让层数增长得慢,每层代价又只有队伍长度那么多,所以整体比一格一格快得多。
← 算法
如果有帮助,请支持我们