排序:将杂乱无章的数据,按照一定的方法进行排列的过程叫做排序。

排序的分类

排序大的分类可分为内排序外排序,不需要访问外存就能进行排序的叫做内排序。

内排序又可以分为以下几类

  1. 插入排序:直接插入排序、二分法插入排序、希尔排序。
  2. 选择排序:直接选择排序、堆排序。
  3. 交换排序:冒泡排序、快速排序。
  4. 归并排序
  5. 基数排序

排序也可以分为稳定排序不稳定排序

稳定排序:假设在待排序的文件中,存在两个或两个以上的记录具有相同的关键字,在用某种排序法排序后,若这些相同关键字的元素的相对次序仍然不变,则这种排序方法是稳定的。即;若a[i]=a[j],**a[i]a[j]之前,经过排序后a[i]依然在a[j]**之前。冒泡排序、直接插入排序、二分插入排序、归并排序,基数排序都是稳定排序。
不稳定排序:直接选择排序、堆排序、快速排序、希尔排序,猴子排序。

冒泡排序

以升序为例,比较相邻的元素,如果第一个比第二个大,则交换他们两个。如果两个元素一样大,则继续比较下一对。所以冒泡排序是一种稳定排序。

快速排序

选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。快速排序是不稳定排序。

直接插入排序

将序列分为两个部分有序无序,每次处理就是将无序数列的第一个元素与有序数列的元素从后往前逐个进行比较,找出插入位置,将该元素插入到有序数列的合适位置中。如果碰到相等的元素,就会把它插入到想等元素后面,顺序不会改变,所以直接插入排序是稳定排序。

二分插入排序

在直接插入排序的基础上,对有序序列进行划分。例如:序列为$a_0,\cdots,a_{i-1},a_{i}$, 其中$a_0,\cdots,a_{i-1}$为有序序列,取$a_{\frac{i-1}{2}}$,将其与$a_i$比较,即可确定$a_i$的范围$a_0,\cdots,a_{\frac{i-1}{2}}$或者$a_{\frac{i-1}{2}},\cdots,a_{i-1}$。然后继续在已确定的范围内进行二分。范围依次缩小为:$\frac{1}{2}, \frac{1}{4}, \frac{1}{8},\frac{1}{16}$。可快速确定 $a_i$应该插入的位置。二分插入排序也是稳定排序。

希尔排序

将整个序列分割成若干个小的子序列,每个子序列内分别进行插入排序。一般情况下步长取 n/2。直到最后一次步长为 1,即所有元素在一个组中进行排序。由于希尔排序是先将整个序列划分为多个子序列进行排序,相同的元素顺序在这个过程中顺序可能会被打乱,所以希尔排序是不稳定排序。

直接选择排序

从待排序的数据元素中,选出最小或最大的元素与序列第一个数交换。直到所有数据排完。直接选择排序是不稳定排序。例如:{3,3,1},第一次排序就将 1 和第一个 3 交换,想等元素的顺序改变了。

以 n=10 的一个数组 49, 38, 65, 97, 26, 13, 27, 49, 55, 4 为例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
第一次 gap = 10 / 2 = 5

49   38   65   97   26   13   27   49   55   4

分成了五组(49, 13) (38, 27) (65, 49)  (97, 55)  (26, 4)
这样每组排序后就变成了(13, 49)  (27, 38)  (49, 65)  (55, 97)  (4, 26),下同。

第二次 gap = 5 / 2 = 2

排序后


13   27   49   55   4    49   38   65   97   26

第三次 gap = 2 / 2 = 1

4   26   13   27   38    49   49   55   97   65


第四次 gap = 1 / 2 = 0 排序完成得到数组:

4   13   26   27   38    49   49   55   65   97

堆排序

堆排序是一种树形选择排序,是对直接选择排序的有效改进。
最大堆:每个节点的值都大于等于它的孩子节点。
最小堆:每个节点的值都小于等于它的孩子节点。
最大堆第 0 个数据是最大数,最小堆第 0 个数据是最小数。
堆排序是不稳定排序 思想

  1. 建堆
  2. 交换第 0 个数据和最后一个数据,弹出最大或者最小数据。
  3. 重复 1. 2. 直到最后两个节点交换完成。

归并排序

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
如何将两个有序序列合并?(升序)
{a[0]……a[i-1]},{b[0]……b[j-1]}
b[0]<a[0], 取b[0]放入数组c中,然后继续比较数组ab中的第一个元素,直到数组ab中最后一对元素比较完成。 思想

将数组分成二组a,b如果这二组组内的数据都是有序的,那么就可以按照上述方法对这二组数据进行排序。如果这二组数据是无序的?
可以将a,b组各自再分成二组。递归操作,直到每个小组只有一个数据,每个小组只有一个元素所以我们可以认为它已经是有序序列,然后进行合并。
先分解后合并。
归并排序是稳定排序

基数排序

将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。从最低位起从 0-9 依次扫描序列,一边扫描一边将扫描到的数据加到新的序列中,得到一个序列。然后比较高一位,重复上述操作,直到最高位排序完成。数列就变成一个有序序列。基数排序是稳定排序。 以全是二位数的序列举例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
初始序列:
73,22,93,43,55,14,28,65,39,81
首先根据个位数的数值,将他们放到对应的数值下面
0
1 81
2 22
3 73 93 43
4 14
5 55 65
6
7
8 28
9 39
排序好的序列为:
81,22,73,93,43,14,55,65,28,39
再根据十位数数值对上面拼接好的序列进行排序
0
1 14
2 22 28
3 39
4 43
5 55
6 65
7 73
8 81
9 93
最终的有序序列:
14,22,28,39,43,55,65,73,81,93

猴子排序

无限猴子定理:指一只猴子随机在打字机键盘上按键,最后必然可以打出法国国家图书馆的每本图书。

  1. 随机打乱数组,检查是否有序。
  2. 有序则输出,无序则重复上述步骤。

时间复杂度最低 1 次,最高可执行到世界的尽头。。。

常见排序算法及对应的时间复杂度和空间复杂度

排序方法 时间复杂度(平均) 时间复杂度(最坏) 时间复杂度(最好) 空间复杂度 稳定性 复杂性
直接插入排序 O(n2) O(n2) O(n) O(1) 稳定 简单
希尔排序 O(nlog2n) O(n2) O(n) O(1) 不稳定 较复杂
直接选择排序 O(n2) O(n2) O(n2) O(1) 不稳定 简单
堆排序 O(nlog2n) O(nlog2n) O(nlog2n) O(1) 不稳定 较复杂
冒泡排序 O(n2) O(n2) O(n) O(1) 稳定 简单
快速排序 O(nlog2n) O(n2) O(nlog2n) O(nlog2n) 不稳定 较复杂
归并排序 O(nlog2n) O(nlog2n) O(nlog2n) O(n) 稳定 较复杂
基数排序 O(d(n+r)) O(d(n+r)) O(d(n+r)) O(n+r) 稳定 较复杂