Sorting
常见的排序算法
插入排序
Straight Insertion Sort
Runtime: . Memory: . 稳定。
插入排序算法解释
- 基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。
从左到右渐次处理,始终保证当前位置左边的数组是有序的,然后将当前位置的数字插入到左侧的有序数组中。
代码
1 |
|
用模板的写法:
1 |
|
希尔排序
Shell’s Sort
Runtime:~,是n和d的函数. Memory: . 不稳定。
希尔排序算法解释
- 也称作“缩小增量排序”,也是一种属于插入排序类的方法。
- 基本思想是,先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
代码
1 | /* |
用模板的写法:
1 |
|
冒泡排序
Bubble Sort
Runtime: average and worst case. Memory: . 稳定。
冒泡排序算法解释:
- 每次遍历比较表中的相邻元素,如果它们是逆序的话就交换它们的位置。重复n-1次后,列表即排好序。
代码
1 | // 假设列表是数组 |
用模板的方式:
1 | #include <iterator> |
选择排序
Selection Sort
Runtime: average and worst case. Memory: . 稳定。
选择排序算法解释:
- 第一遍遍历从1到n的n个元素,将找到的最小元素与位置1的元素交换。第二遍遍历从2到n的n-1个元素,将找到的最小元素与位置2的元素交换。以此类推。n-1遍后,该列表就排好序了。
代码
1 | void selection_sort(int* A, int n) |
堆排序
Heap Sort
Runtime: . Memory: . 不稳定。
堆排序算法解释
- 左右无序,上下有序的完全二叉树,称为堆。在输出堆顶的最小值之后,使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素中的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。
- 初始建立堆。输出堆顶元素后,调整剩余元素成为一个新的堆。适用于n较大的文件。
- 从一个无序序列堆建堆的过程就是一个反复筛选的过程。若将此序列看成一个完全二叉树,则最后一个非终端结点是第n/2个元素,就从它开始。
- 筛选的过程是自上而下,堆顶元素与其左右子树比较并调整的过程。
代码
1 |
|
使用模板:
1 | #include <iterator> |
STL中的Heap操作
- make_heap() 把一序列元素变成heap。
- push_heap() 添加一个元素到heap。
- pop_heap() 从heap中移除下一个元素。
- sort_heap() 把一个heap转换成一个有序队列,此后就不再是heap了。
归并排序
Merge Sort
Runtime: average and worst case. Memory: Depends. 稳定。
归并排序算法解释:
- 归并排序将数组分成两半,然后分别对其排序,然后再将它们合并到一起。每次的排序操作应用都是这个算法,是合并操作承担了主要的工作。
- 合并操作将两半数组拷入到一个帮助数组里,分别用helperLeft和helperRight记录两半数组各自的头元素。然后遍历helper,把小的那个元素拷到目标数组。最后剩下的元素拷到目标数组后面。
代码
1 | #include <iostream> |
快速排序
Quick Sort
Runtime: average, worst case. Memory: . 不稳定。
快速排序算法解释:
- 在快速排序算法中,我们随机选择一个元素并分割数组,将小于所选元素的元素放在大于所选元素的元素之前。分组操作可以用一系列swap操作高效实现。
- 持续分割数组,数组最终会完全排序。不过我们所选择的元素无法保证接近中值,所以最坏情况下的时间复杂度是。
代码
1 |
|
模板实现的partition:
1 | template<typename ForwardIterator, typename UnaryPredicate> |
基数排序
Radix Sort
Runtime: , n is the number of elements, k is the number of passes of the sorting algorithm. 稳定。
基数排序算法解释:
- 基数排序是利用整型数只有有限数目的比特数的特点,只对正整数(或一些其他的数据结构)进行排序的算法。在基数排序中,我们遍历一个数目的每一个数字,按数字将数目分组。
- 举例来说,如果我们有一列整型数字,可以先按最后一个数字排序,所有0结尾的放在一组。然后我们再对前一个数字继续排序。重复这个过程,直到整个数组都已排序。先从最低位开始排,最后到最高位。
代码
1 |
|
比较
排序方法 | 平均时间 | 最坏情况 | 辅助存储 |
---|---|---|---|
插入排序 | |||
冒泡排序 | |||
简单选择排序 | |||
快速排序 | |||
堆排序 | |||
归并排序 | |||
基数排序 |
- 从平均时间性能而言,快速排序最佳,其所需时间最省,但快速排序在最坏情况下的时间性能不如堆排序和归并排序。而后两者比较的结果是,在n较大时,归并排序所需时间较堆排序省,但它所需的辅助存储量最多。
- 插入排序、冒泡排序、简单选择排序都是简单排序,其中直接插入排序最简单。当序列中的记录“基本有序”或n值较小时,它是最佳的排序方法,因此常将它和其他的排序方法,诸如快速排序、归并排序等结合在一起使用。
- 基数排序适用于n值很大而关键字较小的序列。若关键字也很大,而序列中大多数记录的“最高位关键字”均不同,则亦可先按“最高位关键字”不同将序列分成若干“小”的子序列,而后进行直接插入排序。
- 从方法的稳定性来比较,基数排序是稳定的内部排序方法,所有时间复杂度为的简单排序法也是稳定的。然而,快速排序、堆排序和希尔排序等时间性能较好的排序方法都是不稳定的。
leetcode相关题目
单链表适合用归并排序,双向链表适合用快速排序。
用标准库的sort时,注意活用各种可能的sorting标准。
Merge Sorted Array
Merge Two Sorted Lists
Merge k Sorted Lists
Insertion Sort List
Sort List
First Missing Positive
Sort Colors
Merge Intervals
Insert Interval
Largest Number
Maximum Gap
H-Index
Valid Anagram
Wiggle Sort II
[1] Cracking the Code Interview
[2] leetcode
[3] 数据结构(C语言版)