笔记
排序算法
分类
基本排序 [快速无Bug]
冒泡
插入
高级排序 [必考]
归并
快速
拓扑 Topological
其他重点 [需要研究]
堆排序
桶排序
冒泡排序 [稳定]
耗时: O(n^2)
空间: S(1)
核心思想: 从头开始,相邻两两比较交换,直到尾部
=> 这一轮中最大/小元素 冒泡到 Array 尾部 [横着看 吐泡泡]
选择排序 [非稳定]
耗时: O(n^2/2)
空间: S(1)
和冒泡类似,但不需要频繁两两邻近赋值操作,只需要更新赋值当前最大/小 => 少量赋值
插入排序 [稳定]
耗时: O(n) / O(n^2/2)
空间: S(1)
核心思想: 从第二个元素开始,向前比较,[后部数组] 右/后移
在原后部第一元素位置 覆盖 插入元素
归并排序 [稳定]
耗时: O(nlogn) / O(nlogn)
空间: S(2n)
核心思想: [分治] 将复杂问题拆分成若干的子问题,然后一一解决
一直将子数组拆分成更小的子数组(2路=>2个子数组),直到数组中只有1个元素[先局部有序]
然后再合并各个局部有序子数组
=> mergeSort(){
if (hi <= lo) return; // 递归终止条件
int mid = lo + (hi - lo) / 2; // 切分项
mergeSort(左半)
mergeSort(右半)
merge() // 合并排序
}
快速排序 [非稳]
耗时: O(nlogn) / O(n^2/2)
空间: S(nlogn)
核心思想: [分治] 将数组 根据 切分项V 分为2块,不断交换左侧大于V 及 右侧小于V 的2个数组项i,j
也就是粗排序,交换左右不合规元素 => 区域有序,然后再进行递归切分,实现更细区域有序
最终,递归到底达成全局有序 [反着来的归并排序]
=> quickSort(){
if (hi <= lo) return; // 递归终止条件
partition() // 粗排序+切分项
quickSort(左半)
quickSort(右半)
}
与归并区别:
同
二者很像 [最好情况下将所有内容精确地分成两半。这使它有点像“归并排序”]
满足归并排序——分而治之递归算法:Cn = 2Cn + n 公式 ==> N*lgN
异
1.Partition in-place 就地分区
使用一个额外数组来更容易分区及实现Stable,但这不值得
QS 更快 更节省空间,这是QS优势所在
2.shuffleing 是必要的
对于保证性能来说,失去会导致性能受损。切分项也可以随机
随机性是为了平衡最坏情况下的性能 => 最坏情况下的概率保证
3.排序与递归顺序
QS [先粗排序再递归切分]
MS [先递归切分再合并排序]
区域性有序: 区域内 无序,但 均大于 x / 均小于 x
局部性有序: 局部内 有序, 全局无序
堆排序 [非稳]
耗时: O(nlogn) / O(2nlogn + 2n)
空间: S(n)
用途: 在现实嵌入式较少资源情况下唯一使用的原地排序算法
最重要的是它能在插入、删除 混合操作动态场景下能保持 对数级别的计算时间
数据结构: 优先队列([二叉堆]实现(数组))
二叉堆: [堆有序]的[完全[二叉树]]结构元素,在数组中按层级存储
堆有序: 每个节点都大于等于它的两个子节点时,称为堆有序
二叉树: 空子节点,或者连接指向左右子节点(也是一颗二叉树)
完全二叉树: 完美平衡 => 如果树高 4,则一二三层均是完整左右子树,第四层可以不完美
完全二叉树性质:
1.完全二叉树可根据 节点数 计算 树高 [ N nodes => lgN ]
2.只用数组而不需要指针就可以表示/实现
二叉堆性质:
1.不使用数组索引0
2.从任意节点向上,都能获取一列 非递减的元素
从任意节点向下,都能获取一列 非递增的元素
3.二叉堆中索引位置为 k 的元素的父亲为 k/2 向下取整,其子节点为 2k 及 2k+1
核心思想:
构建 MaxPriorityQueue 二叉堆实现的优先队列[其实数组就行],并重复删除最大元素/堆顶元素
=> 即两阶段: 1.堆构造阶段(堆有序); 2.下沉阶段 (删除堆顶/输出最大元素)
与快排归并区别
异
1.全局无序,条状有序(从下至上/从上至下一条线)
2.通过不断 输出(delete/sink) 全局最大/小 至数组末尾 实现 排序
拓扑排序 [顶点排序]
前提: 必须有向图; 图里没有环; 没有孤岛 【统计前驱/入度; 广度优先搜索遍历;】
耗时: O(n)
空间: S(n)
用途: 理清具有依赖关系的任务 [将图论的顶点按照相连的性质进行排序]
void sort(){
Queue q = new LinkedList();
for(int v = 0; v < V; v++)
if(indegree[v] == 0) q.add(v);
while(!q.isEmpty()){
int v = q.poll();
print(v);
for(int u = 0; u < adj[v].length; u++)
if(--indegree[u] == 0) q.add(u);
}
}
时间复杂度比较图
inplace? stable? worst average best remarks
selection x N^2/2 N^2/2 N^2/2 N次交换
insertion x x N^2/2 N^2/4 N 适用于小量(15)元素
shell x ? ? N tight code, subquadratic
quick x N^2/2 2NlogN NlogN probabilistic guarantee fastest
3-way quick x N^2/2 2NlogN N 针对重复Key提升
merge x NlogN NlogN NlogN 稳定NlogN(2种含义)
heap x 2NlogN 2NlogN NlogN 代码简单 插删混合对数级别
实现源码
@SuppressWarnings({"unused"})
public class Sort {
public static void main(String[] args) {
int[] a = {0, 5, 4, 6, 2, 1};
for (int i : a) {
System.out.println(i);
}
}
static int[] popSort(int[] arr) {
boolean hasChanged = true;
for (int n = 1; n < arr.length && hasChanged; n++) {
hasChanged = false;
for (int i = 0; i < arr.length - n; i++) {
int j = i + 1, tmp;
if (arr[i] > arr[j]) {
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
hasChanged = true;
}
}
}
return arr;
}
static int[] insertSort1(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int k = i;
for (int j = i - 1; j >= 0; j--) {
if (arr[j] > arr[k]) {
int tmp = arr[k];
arr[k] = arr[j];
arr[j] = tmp;
k = j;
}
}
}
return arr;
}
static int[] insertSort2(int[] arr) {
for (int i = 1, j, current; i < arr.length; i++) {
current = arr[i];
for (j = i - 1; j >= 0 && arr[j] > current; j--) {
arr[j + 1] = arr[j];
}
arr[j + 1] = current;
}
return arr;
}
static void mergeSortEntrance(int[] a) {
int[] aux = new int[a.length];
mergeSort(a, aux, 0, a.length - 1);
}
static void mergeSort(int[] a, int[] aux, int lo, int hi) {
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
mergeSort(a, aux, lo, mid);
mergeSort(a, aux, mid + 1, hi);
merge(a, aux, lo, mid, hi);
}
static void merge(int[] a, int[] aux, int lo, int mid, int hi) {
if (hi + 1 - lo >= 0) System.arraycopy(a, lo, aux, lo, hi + 1 - lo);
int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++) {
if (i > mid)
a[k] = aux[j++];
else if (j > hi)
a[k] = aux[i++];
else if (less(aux[j], aux[i]))
a[k] = aux[j++];
else
a[k] = aux[i++];
}
}
static boolean less(int a, int b) {
return a < b;
}
static void quickSortEntrance(int[] a) {
quick(a, 0, a.length - 1);
}
static void quick(int[] a, int lo, int hi) {
if (hi <= lo) return;
int j = partition(a, lo, hi);
quick(a, lo, j - 1);
quick(a, j + 1, hi);
}
static int partition(int[] a, int lo, int hi) {
int i = lo, j = hi + 1;
int v = a[lo];
while (true) {
while (less(a[++i], v)) if (i == hi) break;
while (less(v, a[--j])) if (j == lo) break;
if (i >= j) break;
exch(a, i, j);
}
exch(a, lo, j);
return j;
}
static void exch(int[] a, int left, int right) {
int tmp = a[left];
a[left] = a[right];
a[right] = tmp;
}
static void heapSort(int[] a) {
int N = a.length-1;
for (int k = N / 2; k >= 1; k--) {
sink(a, k, N);
}
while (N > 1) {
exch(a, 1, N);
sink(a, 1, --N);
}
}
static void sink(int[] a, int k, int N) {
while (2 * k <= N) {
int j = 2 * k;
if (j < N && less(a[j], a[j + 1])) j++;
if (!less(a[k], a[j])) break;
exch(a, k, j);
k = j;
}
}
}