持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天
归并排序
把序列分成长度相同的两个子序列,当无法继续往下分时(也就是每个子序列中只有一个数据时),就对子序列进行归并。归并指的是把两个排好序的子序列合并成一个有序序列。该操作会一直重复执行,直到所有子序列都归并为一个整体为止
图解
01
首先,把序列对半分隔
02
分成两段
03
继续分
04
分割完毕。对分割后元素合并
05
把6和4合并,合并后的顺序为[4,6]
06
把3和7合并,合并后的顺序为[3,7]
07
下面,合并[4, 6]和 [3, 7]。合并这种含有多个数字的子序列时,要先比较首位数字,再移动较小的数字
08
4>3,移动3
09
同样的,再次比较剩下的首位数字
10
4<7,移动4
11
6<7,移动6
12
最后移动剩下的7
13
递归执行以上操作,直至合成整体
14
继续比较两个序列首位数字
15
3>1,移动1,继续操作...
16
合并完成,排序完成
解说
在合并两个已排好序的子序列时,只需重复比较首位数据的大小,然后移动较小的数据,因此只需花费和两个子序列的长度相应的运行时间。
将长度为n的序列对半分割直到只有一个数据为止时,可以分成log₂n行,也就是说,总的运行时间为O(nlogn),这与前面讲到的堆排序相同。
快速排序
首先在序列中随机选择一个基准值(pivot),然后将除了基准值以外的数分为“比基准值小的数”和“比基准值大的数”这两个类别,再将其排列成以下形式。
[比基准值小的数]基准值[比基准值大的数]
接着,对两个“[]”中的数据进行排序之后,整体的排序便完成了。对“[]”里面的数据进行排序时同样也会使用快速排序
图解
01
快速排序
02
在序列随机选择一个基准值,这里选4
03
将其他数字和基准值比较,小于基准值左移,大于基准值右移
04
首先,比较3和基准值4
05
3<4,3左移
06
接下来,比较5和基准值4
07
5>4,5右移
08
继续操作,最终结果如图
09
把基准值4插入序列。左边比它小,右边比它大
10
分别对左右数据进行排序
11
两边操作与之前相同
12
随机选择基准值,这次选6
13
重复之前操作...
14
重复之前操作...
15
重复之前操作...
16
选8为基准值
17
重复之前操作...
18
由于7、8、9完成排序,所以5、6、7、8、9也完成排序
19
于是,最初选择的基准值4的右边排序完成
20
左边同理,整体排序完成
补充说明
快速排序是一种“分治法”。它将原本的问题分成两个子问题(比基准值小的数和比基准值大的数),然后再分别解决这两个问题。子问题,也就是子序列完成排序后,再像一开始说明的那样,把他们合并成一个序列,那么对原始序列的排序也就完成了。
不过,解决子问题的时候会再次使用快速排序,甚至在这个快速排序里仍然要使用快速排序。只有在子问题里只剩一个数字的时候,排序才算完成。
像这样,在算法内部继续使用该算法的现象被称为“递归”。归并排序也可看作是一种递归的分治法。
解说
分割子序列时需要选择基准值,如果每次选择的基准值都能使得两个子序列的长度为原本的一半,那么快速排序的运行时间和归并排序的一样,都为O(nlogn)。和归并排序类似,将序列对半分割log₂n次之后,子序列里便只剩下一个数据,这时子序列的排序也就完成了。因此,如果像下图这样一行行地展现根据基准值分割序列的过程,那么总共会有log₂n行。