排序
常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。
稳定性是指排序过程中,原来相同的元素保持原来的相对位置
比如 a[i] = a[j] ,且 i<j , 排序后 i<j 依然成立
1、插入排序
(1) 直接插入排序
时间复杂度:O(n^2),最好 O(n)
空间复杂度:O(1)
稳定性:稳定
- 思路:
将开头元素视作已排序
执行下述过程,直到未排序过程消失:
- 取出未排序部分的开头元素赋给变量 v
- 在已排序部分,将所有比 v 大的元素向后移动一位
- 将已取出的元素 v 插入空位

1 | void InsertSort(int a[], int len) { |
(2) 希尔排序
时间复杂度:平均 O(n^1.3),最坏 O(n^2)
空间复杂度:O(1)
稳定性:不稳定
缩小增量法
n 个记录,增量为 di,则分组
下标 0, di, 2di, 3di, …… 为 1 组
下标 1, di +1, 2di +1, 3di +1, …… 为 1 组
下标 2, di +2, 2di +2, 3di +2, …… 为 1 组
…… …… …… ……
下标 di-1, di+di -1, 2di+di -1, …… 为 1 组

思路:
增量为 d1 时,在各组内,进行排序
减小增量,重新分组,组内排序
- 减小增量:初始:d1 = n/2,则模式:di+1 = di /2
重复第 2 步, 直到 di==1,所有记录在同一组,组内排序
- 排序均为直接插入排序

1 | // ? 按照 ShellSort 定义写出 |
2、交换排序
(3) 冒泡排序
时间复杂度:平均 O(n^2),最坏 O(n^2),最好 O(n)
空间复杂度:O(1)
稳定性:稳定
- 思路:
重复执行下述处理,直到数组中不包含顺序相反的相邻元素:
1、从数组开头开始依次比较相邻两个元素,如果大小关系相反则交换位置

1 | void BubbleSort(int a[], int len){ |
(4) 快速排序
时间复杂度:平均 O(nlogn),最坏 O(n^2)
空间复杂度:平均 O(logn),最坏 O(n)
稳定性:不稳定
思路:
以整个数组为对象执行 QuickSort
QuickSort 流程如下:
1、通过分割将对象局部数组分割为前后两个局部数组
2、对前半部分的局部数组执行 QuickSort
3、对后半部分的局部数组执行 QuickSort第 1 趟快排:
从待排序码中,选出 1 个 K(如 R0.key )
- 将小于 k 的记录移动到左边(左子表),
- 大于 k 的记录移动到右边(右子表),
- 将 k 放在左、右两个子表的分界处
左游历下标:i=0, 右游历下标:j=n-1,
取出分区基准:temp=R[0]- 初始空位 R[0]:在左表中,即 R[i]
重复以下两种扫描,直到 i==j (空位置在左,则 j 扫描;空位置在右,则 i 扫描)
- j 向左扫描,直到 R[j].key < temp.key,
将 R[j]移动到空位 R[i]中,则 R[j]为空,令 i++ - i 向右扫描,直到 R[i].key > temp.key,
将 R[i]移动到空位 R[ j]中,则 R[i]为空,令 j - -
- j 向左扫描,直到 R[j].key < temp.key,
将“分区基准” temp,放到空位 R[i]中
第 2 趟快排:
- 对 K 左、右两个字表,分别执行 1 趟快排 - 4 个子表
… …
- 对 K 左、右两个字表,分别执行 1 趟快排 - 4 个子表
直到:各子表长度 ≤1
1 | // ? 快排划分 |
3、选择排序
(5) 简单选择排序
时间复杂度:平均 O(n^2),最坏 O(n^2)
空间复杂度:O(1)
稳定性:不稳定
思路:
- 重复执行 N-1 次 下述处理:
- 找出未排序部分最小值的位置 minj
- 将 minj 位置的元素与未排序部分的起始元素交换

1 | void SelectSort(int a[], int len){ |
(6) 堆排序
时间复杂度:平均 O(nlogn),最坏 O(nlogn),最好 n(nlogn)
空间复杂度:O(1)
稳定性:不稳定
思路:
将待排序数据建立成大根堆
将待排序记录建成 1 个完全二叉树(从左往右插入),再“从后向前”依次调整 sift
- sift(待调整:x):就是让其满足大根堆
- 判断“待调整 x”是否 >左孩子 && >右孩子
- 是,则无需调整,结束
- 否,继续“调整 x”,即:重复,直到 x 与孩子满足堆序性,或 x 成为叶子
- 判断“待调整 x”是否 >左孩子 && >右孩子
- 从最后结点的父亲开始,最后结点下标:n-1 父亲下标 p= (n-1-1)/2
- “从后向前”:依次调整 p, p-1, p-2, …, 0
- 之后 sift (3),sift(2),sift(1),sift(0),构成大根堆
- “从后向前”:依次调整 p, p-1, p-2, …, 0
- sift(待调整:x):就是让其满足大根堆
重复:选出最大值(堆顶)、并调整剩余部分(较大的孩子上升,空位置下降
以此类推,直到堆无剩余元素
1 | // - 图解 https://www.cnblogs.com/chengxiao/p/6129630.html |
4、其他排序
(7) 归并排序
时间复杂度: O(nlogn) (好、坏、平均)
空间复杂度:O(n)
稳定性:稳定
思路:
以整个数组为对象执行 mergeSort
- mergeSort:
- 将给定包含 n 个元素的局部数组分割成两个局部数组
- 对局部数组分别 mergeSort
- 通过 merge 将两个已排序的数组整合为一个数组
1 | void Merge(int a[], int begin, int mid, int end) { |
(8) 基数排序
时间复杂度: O(d(n+r)) d: d 趟分配和收集
空间复杂度:O(r) r: r 个队列
稳定性:稳定
看图理解:
1 | // ? 辅助函数,求数据的最大位数 |
5、排序比较

