O
(
1
)
<
O
(
l
o
g
2
n
)
<
O
(
n
)
<
O
(
n
l
o
g
2
n
)
<
O
(
n
2
)
<
O
(
n
3
)
<
O
(
2
n
)
<
O
(
n
!
)
<
O
(
n
n
)
O(1)

算法的时间复杂度取决于:问题的规模和数据的状态(例如正序、逆序的数据元素)
O(m)+O(n)=O(max{m,n})O(m)×O(n)=O(mn)线性表:相同数据类型、有限数据元素、序列
顺序表三大操作
| 操作 | 平均比较次数(平均移动元素) | 时间复杂度 |
|---|---|---|
| 插入操作 | n/2 | O(n) |
| 删除操作 | (n-1)/2 | O(n) |
| 查找操作(按值查找) | (n+1)/2 | O(n) |
顺序表的元素地址必须是连续的,单链表的结点内存储单元地址必须是连续的。

// 双循环链表p后面跟一个就是指针,p后面跟两个就是结点的指针
// s的右指针指向p
s->rlink = p;
// s的左指针指向p的左指针指向(也就是两个指针指向相同)
s->llink = p->llink;
// s的左结点的右指针指向s
p->llink->rlink = s;
// p的下一个结点的前向指针指向p的前向指针的指向(也就是两个指针指向相同)
p->next->prev = p->prev;
做题画图必背🔥
顺序表:注意下标和位置的关系,例如有些题说在第i个位置插入元素,则i的范围为: 1≤i≤n+1,从第一个位置到第n+1个位置,n+1也就相当于在表尾追加。

不带头结点的单链表:

带头结点的单链表:

仅有尾指针的单链表:

带头结点的循环单链表:

带尾指针的循环单链表:

带头结点的双链表:

带头结点的循环双链表:

链表插入、删除的本质就是找前驱、后继,插入到哪个位置就找那个位置的前驱和后继,删除哪个位置就找那个位置的前驱和后继。
对于双向循环链表:前驱p->prev,后继p->next 都好找,所以必然插入、删除的时间复杂度为O(1)
在顺序表中删除第i个元素,或者在第i个元素之后插入新元素,时间复杂度都是O(n)
在链表中插入删除元素时间复杂度都是O(1):❗注意❗,我们有时候在题中看到使用链表插入删除的时间复杂度为O(n),这其实包含两个操作:查找这个结点:O(n),删除这个结点:O(1)。所以我们常说链式存储方式能更快的实现插入和删除操作。
例如:在已知头指针的单链表中,要在尾部插入一个新结点和删除第一个元素,其时间复杂度为O(1)+O(n)
删除第一个元素,就找第一个元素的前驱和后继。插入一个新结点,就找新结点位置的前驱和后继。

O(1)+O(n)
O(1)+O(n)
O(1)
顺序表按值查找O(n),按位查找O(1),链表无论是按值还是按位查找都是O(n)
可以用 抽象数据类型ADT 来定义一个完整的数据结构。
线性结构(一对一):线性表、栈、队列、双队列、数组、串,非线性结构(非一对一):二维数组、多维数组、广义表、树、图,顺序存储结构不仅可以存储线性结构,也可以存储非线性结构,例如树、图等。
有序表:关键字有序的线性表,仅描述元素间的逻辑关系,不在乎存储结构,因为只需要描述有顺序的逻辑即可。
存储数据时,不仅要存储各数据元素的值,而且要存储数据元素之间的关系。
计算时间复杂度问题:若内层外层没有关系,可以先算外层再算内层,然后相乘。若内层外层有关系,则通过外层来计算内层执行次数,最终将执行次数相加。
头指针为L,带头结点的单链表为空:L->next=null ,不带头结点的单链表为空:L=null
L->prior ==L && L->next ==L ,头指针的前驱和后继都指向自身在插入、删除操作中,双链表可以更容易找到前驱和后继,虽然花费的时间少了,但是因为修改了更多的指针指向,所以相比起单链表,操作更复杂了。相当于用更多的操作换取更少的时间。
静态链表
静态链表需要分配比较大的连续空间
静态链表在插入、删除元素时不需要移动元素,只需要修改指针

头指针必有,有头结点就指向头结点;没头结点就指向第一个数据元素
分析题画图必备🔥
栈顶指针分为两种:一种是栈顶指针指向栈顶元素,另一种是栈顶指针指向栈顶元素的下一个位置
这种情况下初始化:top=-1
这种情况下判断是否为空栈:判断top指针是否是-1
这钟情况下入栈:先让top++,然后将元素放入top所指向的 a1 处
这种情况下出栈:先让元素出栈,然后top- -
这种情况下栈满:top=MaxSize-1

这种情况下初始化:top=0
这种情况下判断是否为空栈:判断top指针是否是0
这钟情况下入栈:先将元素a1放入top所指向的位置,然后 top++
这种情况下出栈:先让top- -,然后将元素出栈
这种情况下栈满:top=MaxSize

上述情况也适合不带头结点的链栈,对于带头结点的链栈:top指针指向的就是头结点

不带头结点的链栈,top指针指向第一个数据元素,带头结点的链栈,top指针指向头结点
共享栈的好处是:节省存储空间,降低发生上溢的可能
栈的空间是 0~预设空间上限,所以只可能发生上溢。
当第一个栈顶指针top1初值是-1,第二个栈顶指针top2初值为n。栈满的条件为top2-top1=1

top1-top2=1
队列有两种:一种是队尾指针 rear 指向队尾元素的下一个位置(循环队列),一种是队尾指针指向队尾元素
队头指针 front 指向队头元素,队尾指针 rear 指向队尾元素的后一个位置(下图红色单元不存储元素)
初始化队列:队头、队尾指针指向0 rear = = front = = 0
判断队列是否为空:队头、队尾指针是否指向0
入队:将元素放入队尾指针所指向的位置,然后再将队尾指针rear向后移动一位(入队队尾指针rear++),入队操作:(rear+1) % MaxSize 。【这里的MaxSize就是队列的最大容量,例如队列是数组A[0,n],那么容量就为n+1】。所以队尾指针 rear 其实是从队头指向队尾,再从队尾指向队头,这样循环移动
出队:front指针依次向后移动,当front指针和rear指针指向相同,则队列为空(出队队头指针front++),出队操作:(front+1) % MaxSize
队满的条件:队尾指针+1=队头指针 (Q.rear+1) % MAXSIZE == Q.front
队空的条件:队尾指针和队头指针指向相同均指向0 Q.rear == Q.front ==0
队列元素的个数:(队尾指针+最大队元素-队头指针)对最大队元素取余 (rear+MaxSize-front) % MaxSize

队尾指针指向队尾元素
初始化队列:队头指针指向0,队尾指针指向n-1的位置 rear = n-1, front = 0
判断队列是否为空:队尾指针的下一个位置是不是队头指针 (rear+1) % MaxSize = front
入队:队尾指针rear++,然后放入元素。入队操作:(rear+1) % MaxSize 。
出队:front指针依次向后移动,当front指针和rear指针指向相同,则队列为空(出队队头指针front++),出队操作:(front+1) % MaxSize
队满的条件:队尾指针+2=队头指针 (Q.rear+2) % MAXSIZE == Q.front
队空的条件:队尾指针的下一个位置指向队头 (rear+1) % MaxSize = front

上述两种情况都是牺牲一个存储单元,牺牲的都是队尾的最后一个单元。
也有些题牺牲队头的第一个单元,例如循环队列,front指向队头元素的前一个位置1,rear指向队尾元素,这种情况下牺牲的就是第一个单元。【王道队列选择第6题】
当然对于上述循环队列判断队满,有三种方式可以判断
(Q.rear+1) % MAXSIZE == Q.frontQ.rear == Q.front(rear+MaxSize-front) % MaxSizeQ.size == 0Q.size == MaxSizeQ.rear==Q.front,则为队空Q.rear==Q.front,则为队满栈的应用

中缀转后缀机算:从左到右扫描🔥【真题考过】
( 直接入栈;遇到右括号 ) 则依次弹出栈内运算符并加入后缀表达式,直到弹出左括号( 为止。注意:左括号( 不加入后缀表达式。( 或栈空则停止。之后再把当前运算符入栈。
中缀转前缀手算:根据右优先确定中缀表达式的运算符的先后顺序,按照 [左操作数 右操作数 运算符] 的方式组合成一个新的操作数【真题没考过】
后缀转中缀:从左往右扫描,每遇到一个运算符,就让运算符插入前面最近的两个操作数之间,然后操作数两边带上括号【真题没考过】

后缀表达式手算:给你一个后缀表达式,计算它的值。从左往右扫描,每遇到一个运算符,就让运算符前面最近的两个操作数执行对应运算,合体为一个操作数。【真题没考过】
后缀表达式机算【真题考过】
中缀表达式机算:初始化两个栈,操作数栈和运算符栈。🔥

栈的应用:
队列的应用:
缓冲区:打印机缓冲区等
页面置换算法
串的存储方式有两种,顺序存储和链式存储,顺序存储由分为定长存储和堆分配存储(可以改变大小)。
串的模式匹配:在主串中找到与模式串相同的子串,并返回其所在位置。
朴素模式匹配算法(简单模式匹配算法)思想:将主串中的模式串长度相同的子串搞出来,挨个与模式串对比,当子串与模式串某个对应字符不匹配时,就立即放弃当前子串,转而检索下一个子串
若模式串长度为 m,主串长度为 n,则直到匹配成功/匹配失败最多需要 (n-m+1)*m 次比较
朴素模式匹配算法的缺点:当某些子串与模式串能部分匹配时,主串的扫描指针i经常回溯,导致时间开销增加。最坏的时间复杂度O(nm)
KMP算法:当子串和模式串不匹配时,主串指针i不回溯,模式串指针 j=next[j] ,算法的平均时间复杂度:O(n+m)
KMP算法:
'abaabc' ,若 next数组第一位是-1,则序号从0开始,next数组 = 最长前后缀相等长度| 序号 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | a | b | c |
| next数组 | -1 |
'abaabc' ,若 next数组第一位是0,则序号从1开始,next数组 = 最长前后缀相等长度 + 1| 序号 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | a | b | c |
| next数组 | 0 |
搞懂next数组存在的意义,当匹配失败时,模式串的指针指向next数组处。
例如主串T='abaabaabcabaabc',模式串为 'abaabc' ,采用KMP算法进行模式匹配
| 序号 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 模式串 | a | b | a | a | b | c |
| next数组 | -1 | 0 | 0 | 1 | 1 | 2 |
aabc 从第3个a开始和主串匹配
度为2的树和二叉树的区别:
二叉树是有序树,即使树中结点只有一棵子树,也要区分它是左子树还是右子树。
度为2的有序树就是二叉树。(×)在二叉树中,若某个结点只有一个孩子,则这个孩子的左右次序是确定的,而在度为2的有序树中,若某个结点只有一个孩子,则这个孩子就无序区分左右次序。
树的结点数为n,边数是n-1,再添一条边一定会形成一个环。
n 个结点的二叉树,有 n + 1 个空链域。我们可以利用这些空链域来记录前驱、后继的信息。
对于任何一棵二叉树,高度可能为 ⌈ log2 (n+1)⌉ ~ n 或 ⌊log2 n⌋ + 1 ~ n
若二叉树采用二叉链表结构,则链表中只有孩子结点的地址,而无双亲结点的地址,而遍历过程中又需要结点的双亲结点的地址,为此,遍历操作设置一个栈来达到这个目的。如果不设置栈,则需要采用三叉链表结构,因此三叉链表中除了孩子结点的地址以外,还保存了结点的双亲结点的地址。
前序序列和后序序列不能唯一的确定一棵二叉树,但是可以确定二叉树中结点的祖先关系。当两个结点的前序序列为XY,后续序列为YX时,则X为Y的祖先。
树的性质:
结点数 = 总度数(总边数)+1
度为 m 的树第 i 层至多有 mi-1 个结点
假
设
每
层
都
是
满
的
第
一
层
m
0
第
二
层
m
1
第
三
层
m
2
.
.
.
.
第
i
层
m
i
−
1
假设每层都是满的 \\ 第一层m^0 \\ 第二层m^1 \\ 第三层m^2 \\ .... \\ 第i层m^{i-1}
假设每层都是满的第一层m0第二层m1第三层m2....第i层mi−1
高度为 h 的 m 叉树至多有 (mh -1)/(m-1) 个结点
假
设
每
层
都
是
满
的
m
0
+
m
1
+
m
2
+
.
.
.
+
m
h
−
1
=
m
h
−
1
m
−
1
假设每层都是满的 \\ m^0+m^1+m^2+...+m^{h-1} = \frac{m^h-1}{m-1}
假设每层都是满的m0+m1+m2+...+mh−1=m−1mh−1
具有n个结点的m叉树的最小高度为
最
小
高
度
也
就
是
每
层
都
是
满
的
m
h
−
1
m
−
1
=
n
h
=
[
l
o
g
m
(
n
(
m
−
1
)
+
1
)
]
之
所
以
向
上
取
整
的
原
因
是
即
使
最
后
一
层
有
一
个
结
点
也
得
算
一
个
高
度
最小高度也就是每层都是满的 \\ \frac{m^h-1}{m-1} = n \\ h=[log_m(n(m-1)+1)] \\ 之所以向上取整的原因是即使最后一层有一个结点也得算一个高度
最小高度也就是每层都是满的m−1mh−1=nh=[logm(n(m−1)+1)]之所以向上取整的原因是即使最后一层有一个结点也得算一个高度
高度为 h 的 m 叉树至少有 h 个结点
高度为 h、度为 m 的树至少有 h+m-1 个结点
二叉树的性质:
完全二叉树的性质:

只有最后两层可能有叶子结点。对于最大层次中的叶子结点,都依次排列在该层最左边的位置上。
同时只能有一个度为1的结点,且该结点只有左孩子而无右孩子。
按层序编号后,某结点 i 为叶子结点或只有左孩子,则编号大于 i 的结点均为叶子结点。
若n为奇数,则每个分支结点都有左孩子和右孩子,若n为偶数,则编号最大的分支结点(编号为n/2)只有左孩子,没有右孩子,其他分支结点左、右孩子都有。
若结点 i ≤ ⌊n/2⌋ ,则结点为分支结点,否则为叶子结点
i
≤
⌊
n
2
⌋
为
分
支
节
点
例
如
结
点
6
≤
12
2
,
则
结
点
6
为
分
支
结
点
i ≤ ⌊\frac{n}{2}⌋ 为分支节点 \\ 例如结点6 ≤ \frac{12}{2} ,则结点6为分支结点
i≤⌊2n⌋为分支节点例如结点6≤212,则结点6为分支结点
第一层有20个结点,第二层有21个结点,第三层有22个结点…
按层序从 1 开始编号,结点 i 的左孩子为 2i,右孩子为 2i+1,结点 i 的父结点为 ⌊i/2⌋
结点 i 所在所在层次(高度/深度)为 ⌊log2 i⌋ +1
n个结点的完全二叉树的高度为 ⌈ log2 (n+1)⌉ 或 ⌊log2 n⌋ + 1
完全二叉树中n1=0或1
{
n
0
+
n
1
+
n
2
=
总
结
点
数
n
0
=
n
2
+
1
( 2 n 2 + 1 ) + n 1 = 总 结 点 数 若 总 结 点 数 为 偶 数 , 因 为 ( 2 n 2 + 1 ) 为 奇 数 , 则 n 1 = 1 , n 0 = k , n 2 = k − 1 若 总 结 点 数 为 奇 数 , 因 为 ( 2 n 2 + 1 ) 为 奇 数 , 则 n 1 = 0 , n 0 = k , n 2 = k − 1 (2n_2+1)+n_1=总结点数 \\ 若总结点数为偶数,因为(2n_2+1)为奇数,则n_1=1,n_0=k,n_2=k-1 \\ 若总结点数为奇数,因为(2n_2+1)为奇数,则n_1=0,n_0=k,n_2=k-1 (2n2+1)+n1=总结点数若总结点数为偶数,因为(2n2+1)为奇数,则n1=1,n0=k,n2=k−1若总结点数为奇数,因为(2n2+1)为奇数,则n1=0,n0=k,n2=k−1
满二叉树的性质:一棵高度为 h,且含有 2h -1 个结点的二叉树

线索二叉树:
所以对二叉树进行中序线索化,我们就先对二叉树进行中序遍历,然后对结点进行线索:结点若无左右孩子,则左指针指向中序序列的前驱,右指针指向中序序列的后继。
二叉排序树:左子树<根节点<右子树
二叉排序树的插入:若原二叉排序树为空,则直接插入结点;否则,若关键字k小于根节点值,则插入到左子树,若关键字k大于根节点值,则插入到右子树。注意:新插入的结点一定是叶子结点
二叉排序树的构造:不同的关键字序列可能得到同款二叉排序树,也可能得到不同款二叉排序树
二叉排序树的删除:
若被删除结点z是叶子结点,则直接删除,不会破坏二叉排序树的性质
若结点z只有一棵左子树或右子树,则让z的子树替代z的位置
若结点z有左、右两棵子树,
若被删除结点是叶子结点,则直接删除,即使再插入该结点,插入结点后的二叉排序树与删除之前相同
若删除的不是叶子结点,则再插入该删除的结点的二叉排序树会发生变化
平衡二叉树的构建:按照二叉排序树进行构建,边构建边平衡。平衡二叉树的本质上也是一种二叉排序树。
平衡二叉树的调整:失衡结点我称为第一个结点
解释:LL : 在失衡结点的左子树的左子树上面插入导致不平衡
哈夫曼树:
树与二叉树之间的转换:
| 树 | 二叉树 | 森林 |
|---|---|---|
| 先根遍历 | 先序遍历 | 先序遍历 |
| 后根遍历 | 中序遍历 | 后序遍历 |
线性表可以是空表,树可以是空树,但图不可以是空,即图中至少要有一个顶点。
顶点的度:顶点v的度是指连接到该顶点的边数
无向图的全部顶点n的度的和等于边数e的2倍
有向图的全部顶点的度等于该顶点的入度和出度之和

连通与强连通
无向图中任意两个顶点之间都有路径存在,则称无向图为连通图
有向图中任意两个顶点之间都有弧存在,则称有向图为强连通图。
对于n个顶点的无向图G:
对于n个顶点的有向图G:

| 邻接矩阵 | 邻接表 | 十字链表 | 邻接多重表 | |
|---|---|---|---|---|
| 空间复杂度 | O(n2) | 无向图O(n+2e);有向图O(n+e) | O(n+e) | O(n+e) |
| 适合用于 | 存储稠密图 | 存储稀疏图 | 只能存有向图 | 只能存无向图 |
| 表示方式 | 唯一 | 不唯一 | 不唯一 | 不唯一 |
| 找相邻的边 | 必须遍历对应行或列,时间复杂度为O(n) | 找有向图的入边必须遍历整个邻接表,其余很方便 | 很方便 | 很方便 |
| 删除边或顶点 | 删除边很方便,删除顶点需要大量移动数据 | 无向图中删除边或顶点都不方便 | 很方便 | 很方便 |
| 计算度/出度/入度 | 必须遍历对应行或列 | 计算有向图的度、入度不方便,其余很方便 |
邻接矩阵:
对于无向图:第i个结点的度=第i行(或第i列)的非零元素个数。
对于有向图:
无向图的邻接矩阵是对称矩阵(所以可以采用压缩存储,只存储上/下三角区),有向图的邻接矩阵可能不是对称矩阵
邻接矩阵法求顶点的度/出度/入度的时间复杂度是O(n)
邻接矩阵为A,则An 的元素An[i] [j] 等于由顶点 i 到顶点 j 的长度为n的路径的数目
邻接矩阵唯一
邻接表:
| 广度优先遍历 | 深度优先遍历 | |
|---|---|---|
| 空间复杂度 | O(n) | O(1)-O(n) |
| 时间复杂度 | 邻接矩阵:O(n2) 邻接表:O(n+e) | 邻接矩阵:O(n2) 邻接表:O(n+e) |
| 树 | 层次遍历 | 先根遍历 |
采用邻接矩阵和邻接表存储会影响广度/深度遍历序列的唯一性:
给定一个无向网,在该网中的所有生成树中,使得各边权值之和最小的那棵生成树称为该网的最小生成树,也叫最小代价生成树。
| 算法名 | 普里姆算法 | 克鲁斯卡尔算法 |
|---|---|---|
| 算法思想 | 选择点 | 选择边 |
| 时间复杂度 | O(N2) | O(elog2e)(e为边数) |
| 适应范围 | 稠密图 | 稀疏图 |
连通图的生成树是包含图中全部顶点的一个极小连通子图(边尽可能的少,但是要保持连通)。
若图中顶点数为n,则它的生成树含有n-1条边。对生成树而言,若砍去它的一条边,则会变成非连通图,若加上一条边则会形成一个回路。
生成树不唯一,可能有多个
| BFS算法 | Dijkstra算法 | Floyd算法 | |
|---|---|---|---|
| 无权图 | √ | √ | √ |
| 带权图 | × | √ | √ |
| 带负权值的图 | × | × | √ |
| 带负权回路的图 | × | × | × |
| 时间复杂度 | O(n2)或O(n+e) | O(n2) | O(n3) |
| 通常用于 | 求无权图的单源最短路径 | 求带权图的单源最短路径 | 求带权图中各顶点间的最短路径 |
注:也可用 Dijkstra 算法求所有顶点间的最短路径,重复 n 次即可,总的时间复杂度也是 O(n3)
折半查找判定树是平衡二叉排序树
折半查找的判定树中,只有最下面一层是不满的,因此元素个数为 n 时树高 h
树 高 h = ⌈ l o g 2 ( n + 1 ) ⌉ 和 计 算 完 全 二 叉 树 高 度 计 算 方 法 相 同 ( 这 里 折 半 查 找 判 定 树 不 包 括 失 败 结 点 , 若 包 含 失 败 结 点 , 则 树 高 = h + 1 树高 h = ⌈ log_2(n +1)⌉ \\ \\ 和计算完全二叉树高度计算方法相同(这里折半查找判定树不包括失败结点,若包含失败结点,则树高 = h+1 树高h=⌈log2(n+1)⌉和计算完全二叉树高度计算方法相同(这里折半查找判定树不包括失败结点,若包含失败结点,则树高=h+1
折半查找的平均查找长度ASL ≤ 树高h(不包含失败结点的树高)
折半查找的时间复杂度 = O(log2n)
对于有n个成功结点的判定树,失败结点个数为n+1
| BST二叉排序树 | AVL平衡二叉树 | RBT红黑树 | |
|---|---|---|---|
| 查找 | O(n) | O(log2n) | O(log2n) |
| 插入 | O(n) | O(log2n) | O(log2n) |
| 删除 | O(n) | O(log2n) | O(log2n) |
左根右,根叶黑,不红红,黑路同
- 左根右:左子树结点值 ≤ 根结点值 ≤ 右子树结点值
- 根叶黑:根结点和叶节点必须是黑色的
- 不红红:两个红结点不能连接
- 黑路同:从任一非叶结点到任一叶结点的路径上黑结点数目相同

m阶B树中:

m阶B+树中:
| m阶B树 | m阶B+树 | |
|---|---|---|
| 关键字与分叉 | n个关键字对应n+1个分叉(子树) | n个关键字对应n个分叉 |
| 结点包含信息 | 所有结点中都包含记录的信息 | 只有最下层叶子结点才包含记录的信息 |
| 查找方式 | 随机查找,不支持顺序查找 | 顺序查找、随机查找均可 |
相同点:任何一个结点的子树都要一样高(保证绝对平衡)
直接插入排序:每次将一个待排序的记录按其关键字大小插入到前面已经排好序的子序列中
空间复杂度:O(1)
时间复杂度:主要来自对比关键字、移动元素。若有n个元素,则需要 n-1 趟处理
平均时间复杂度 = O(n2),算法稳定性:稳定
适用于顺序表、链表
第一趟排序使得前2个元素有序,第二趟排序使得前3个元素有序
折半插入排序:先用折半查找找到应该插入的位置,再移动元素
第一趟冒泡排序使关键字值最小的一个元素在最前面,第二趟冒泡排序使关键字最小的两个元素在最前面
空间复杂度 = O(递归层数)
时间复杂度 = O(n×递归层数)
稳定性:不稳定!
若初始序列有序或逆序,则快速排序的性能最差(每次选择的都是最靠边的元素),若每一次选中的枢轴将带排序序列划分为很不均匀的两个部分,则会导致递归深度增加算法效率变低。若每一次选中的枢轴将带排序序列划分为均匀的两个部分,则递归深度最小,算法效率最高。
n个元素的简单选择排序需要 n-1 趟处理,无论有序、逆序还是乱序,一定需要 n-1 趟处理.
空间复杂度: O(1)
时间复杂度:O(n2)
稳定性:不稳定!
适用性:顺序表、链表
第一趟选最小/大的元素加入有序队列,第二趟选剩余元素的最小/大的元素加入有序队列
结论:
堆排序的时间复杂度 = 建堆的时间复杂度O(n) + 排序复杂度O(nlog2n) = O(nlog2n)
堆排序的空间复杂度 = O(1)
稳定性:不稳定!
"2路"归并:就是每次只需对比关键字1次就可以选出一个小元素
n个元素进行2路归并排序,归并趟数 = ⌈log2n⌉
每趟规定时间复杂度为O(n),则算法时间复杂度为O(nlog2n)
空间复杂度为 O(n)
稳定性:稳定!
2路归并排序:第一趟每两个元素有序,第二趟每四个元素有序
空间复杂度:O®,需要 r 个辅助队列
时间复杂度 = O(d(n+r))
稳定性:稳定!