树
- 树的表示法:双亲表示法、孩子表示法、孩子兄弟表示法
- 树与二叉树的转化
- 树的遍历:先根、后根
- 森林与二叉树的转化
- 森林的遍历:先序、中序、后序
图
算法 | 邻接矩阵 | 邻接表 | 思想 | 用途 |
---|---|---|---|---|
DFS/BFS | O(V2)O(V^2)O(V2) | O(V+E)O(V+E)O(V+E) | - | 图的遍历 |
Prim | O(V2)O(V^2)O(V2) | O(V+E)O(V+E)O(V+E) | 点贪心+BFS | 最小生成树 |
Kruskal | O(ElogE)O(E\log{E})O(ElogE) | O(ElogE)O(E\log{E})O(ElogE) | 边贪心 | 最小生成树 |
Dijkstra | O(V2)O(V^2)O(V2) | O(V+E)O(V+E)O(V+E) | 贪心+BFS | 单源最短路 |
Floyd | O(V3)O(V^3)O(V3) | - | DP | 多源最短路 |
Topo | O(V2)O(V^2)O(V2) | O(V+E)O(V+E)O(V+E) | - | AOV,AOE |
关键路径 | O(V2)O(V^2)O(V2) | O(V+E)O(V+E)O(V+E) | BFS | - |
查找
查找方式 | ASL |
---|---|
顺序查找 | (n+1)2\frac{(n+1)}{2}2(n+1) |
折半查找 | 接近于logn+1\log{n}+1logn+1* |
分块(索引)查找 | 如果使用顺序查找,则是d2+n2d+1\frac{d}{2}+\frac{n}{2d}+12d+2dn+1,最优分块ddd是n\sqrt{n}n |
哈希查找 | 散列函数、处理冲突方式、装填因子α\alphaα有关 |
*可以利用二叉判定树的性质证明。
查找数据结构
上述的查找方式基本上是静态查找,以下的数据结构可以用于动态查找。自己看书学一学就会了。
BST
- 增加元素
在查找失败的叶子节点处,增加一个新的节点就好了。 - 删除元素
如果是叶子节点,则直接删除。
如果是非叶子节点,则用直接前驱或后继替代当前节点的位置,然后转而将直接前驱或后继删除,这样递归下去的话,一定能转化为删除叶子节点。
AVL
由于BST对于不同插入序列构造出来的BST都不同。所以可能会退化为链表。
通过旋转操作使得BST平衡。操作与BST树相同,只是多了个旋转操作。
基本概念:
- 平衡因子:
右子树高度减去左子树高度 - 平衡:
平衡因子在[−1,1][-1,1][−1,1]之间。 - 旋转
分为LL,LR,RR,RL旋转
B-树
- 查找元素
与BST类似 - 增加元素
与BST相同,若节点个数为m,则需要分裂。 - 删除元素
与BST相同。
如果节点个数为m/2-1,则需要向兄弟节点借节点。
如果兄弟不够借,则兄弟与父节点合并。如果合并导致高度变化,则用爷爷与父亲兄弟合并,来增加树的高度。 - B-树元素与高度之间的关系
高度为hhh的B树含有元素最多有mh−1m^h-1mh−1个节点。最少含有2m2h−1+m2−32\frac{m}{2}^{h-1}+\frac{m}{2}-322mh−1+2m−3个节点。
B+树
可以索引查找,也可以顺序查找
排序
排序名称 | 平均时间复杂度 | 最好时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 | 元素比较次数 | 原始序列 |
---|---|---|---|---|---|---|---|
插入排序 | O(n2)O(n^2)O(n2) | O(n)O(n)O(n) | O(n2)O(n^2)O(n2) | O(1)O(1)O(1) | 稳定 | (n−1)(n-1)(n−1)-((n−1)n2)(\frac{(n-1)n}{2})(2(n−1)n) | 有关 |
希尔排序 | O(n1.3)O(n^{1.3})O(n1.3) | O(n)O(n)O(n) | O(n2)O(n^2)O(n2) | O(1)O(1)O(1) | 不稳定 | - | 有关 |
冒泡排序 | O(n2)O(n^2)O(n2) | O(n)O(n)O(n) | O(n2)O(n^2)O(n2) | O(1)O(1)O(1) | 稳定 | (n−1)(n-1)(n−1)-((n−1)n2)(\frac{(n-1)n}{2})(2(n−1)n) | 有关 |
快速排序 | O(nlogn)O(n\log{n})O(nlogn) | O(nlogn)O(n\log{n})O(nlogn) | O(n2)O(n^2)O(n2) | O(logn)O(\log{n})O(logn)-O(n)O(n)O(n) | 不稳定 | - | 有关 |
选择排序 | O(n2)O(n^2)O(n2) | O(n2)O(n^2)O(n2) | O(n2)O(n^2)O(n2) | O(1)O(1)O(1) | 不稳定 | (n−1)n2\frac{(n-1)n}{2}2(n−1)n | 无关 |
堆排序 | O(nlogn)O(n\log{n})O(nlogn) | O(nlogn)O(n\log{n})O(nlogn) | O(nlogn)O(n\log{n})O(nlogn) | O(1)O(1)O(1) | 不稳定 | - | 无关 |
归并排序 | O(nlogn)O(n\log{n})O(nlogn) | O(nlogn)O(n\log{n})O(nlogn) | O(nlogn)O(n\log{n})O(nlogn) | O(n)O(n)O(n) | 稳定 | - | 无关 |
基数排序 | O(d(r+n))O(d(r+n))O(d(r+n)) | O(d(r+n))O(d(r+n))O(d(r+n)) | O(d(r+n))O(d(r+n))O(d(r+n)) | O(r)O(r)O(r) | 稳定 | 000 | 无关 |
排序概述
- 时间复杂度与空间复杂度
- 稳定性:不用来判断排序算法的优劣,只是一种需求。
- 内部排序主要有
插入排序:将要排序的元素插入到已经有序的序列中
交换排序:不断的交换元素使得序列有序
选择排序:每次选择一个元素放到最终的位置上
归并排序:有序表的合并
基数排序:不基于比较元素的排序
插入排序
简单插入排序
def:插入排序是通过将当前元素插入之前有序列表的排序方法。
特点:
- 排序速度受到原始序列的影响。当基本有序的时候,速度接近O(n)O(n)O(n),当接近逆序的时候,速度接近O(n2)O(n^2)O(n2)
- 稳定
优化:折半排入排序,因为当前插入元素之前的元素都是有序的,所以我们可以通过二分查找来替代顺序查找。但是,该方法只能降低元素的比较次数,而不能降低元素的移动次数。所以平均时间复杂度还是O(n2)O(n^2)O(n2)。
希尔排序(分组插入排序)
原理:因为当序列是基本有序的时候,简单插入排序的效率是线性的。所以可以通过分组插入的方法,让序列不断的接近有序的状态从而降低排序时间。
实现:通过一个因子ddd来控制组内元素间隔的距离。并且不断的减小ddd,最后使得d=1d=1d=1的时候,退化为直接插入排序。
void ShellSort(int *a,int n){
for(int d = n/2;d>=1;d/=2){ //控制距离的因子d
for(int i=d;i<n;i++){ //组内使用直接插入排序
int tmp = a[i] , j;
for(j = i-d;j>=0&&a[j]>tmp;j-=d)
a[j+d] = a[j];
a[j+d] = tmp;
}
}
}
交换排序
冒泡排序
特点:
- 排序的趟数、排序速度与初始序列有关。
- 每趟排序之后,一定有一个元素在最终的位置上。
快速排序
思想:分块
递归调用函数的时候,每次都把区间分成左右区间。左区间的元素个数全部小于该元素,右区间的元素全部大于等于该元素。(相等元素出现的位置与代码有关)
特点:
- 排序的趟数、速度、空间与初始序列有关
- 每趟排序之后,一定有一个元素在最终的位置上;在同一层的总用时是O(n)O(n)O(n)
代码:
int partition(int *a,int l,int r){
int tmp = a[l]; //基准元素
//! l不能先++
while(l<r){
while(l<r&&a[r]>=tmp) r--;
a[l] = a[r];
while(l<r&&a[l]<tmp) l++;
a[r] = a[l];
}
a[l] = tmp;
return l;
}
void qsort(int *a,int l,int r){
if(l<r){
int mid = partition(a,l,r); //分割区间
qsort(a,l,mid-1); //排序左边
qsort(a,mid+1,r); //排序右边
}
}
选择排序
简单选择排序
特点:
- 简单排序算法中唯一一个不稳定的算法。
- 排序时间与原始序列无关。
堆排序
通过数据结构堆(heap)的排序。
时间复杂度分析:
- 建堆O(n)O(n)O(n),从第一个非叶子节点开始向下调整。考虑每次都调整到叶子节点,调整的次数为Tn=20×h+21×(h−1)+22×(h−2)+⋯+2h×1Tn=2^0\times h +2^1\times (h-1)+2^2\times (h-2)+\dots+2^h\times 1Tn=20×h+21×(h−1)+22×(h−2)+⋯+2h×1,计算之后发现是O(n)O(n)O(n)的
- 排序O(nlogn)O(n\log{n})O(nlogn),每取一个元素是O(logn)O(\log{n})O(logn),取nnn个元素当然是O(nlogn)O(n\log{n})O(nlogn)的。
所以时间复杂度是O(nlogn)O(n\log{n})O(nlogn)的。
特点:
- 简单排序算法中唯一一个不稳定的算法。
- 排序时间与原始序列无关。只会影响常数的大小。
归并排序
基于有序表的合并算法。需要额外开辟O(n)O(n)O(n)个辅助空间。
特点:
- 与原始序列无关,算法稳定。
- 可以用于外部排序
基数排序
通过分散和收集两个操作来完成。
有两种排序的方法:低位优先和高位优先。
同一优先级中,通过其他不同的优先级来区分。