一、数组
1. 数据结构定义
顺序
struct SqList{
int length;
ElemType data[MaxSize];
};
链式(链表)
单链表
typedef LNode{
ElemType data;
struct LNode * next;
}LNode,*LinkList;
双链表
typedef DNode{
ElemType data;
struct DNode *pre, *next;
}DNode,*DLinkList;
2.矩阵压缩存储
(1)对称矩阵
定义: a [ i ] [ j ] = = a [ j ] [ i ] {a[i][j] == a[j][i]} a[i][j]==a[j][i]
实例
行优先压缩:
压缩存储下三角矩阵,第
i
{i}
i 行存
i
{i}
i 个元素,依次存入一维数组。
下标对应公式:
设
n
∗
n
n * n
n∗n 二维矩阵
a
[
i
]
[
j
]
{a[i][j]}
a[i][j] 压缩到 一维矩阵
A
[
w
]
{A[w]}
A[w] 中,A下标从0开始,有
i f ( i < j ) s w a p ( i , j ) {if(i < j) ~~~ swap (i,j)} if(i<j) swap(i,j)
w = ∑ 1 i − 1 + j − 1 {w = \sum_{1}^{i-1} + j - 1} w=∑1i−1+j−1
列优先压缩:
压缩存储下三角矩阵,第
j
{j}
j 列存
n
−
j
+
1
{n-j+1}
n−j+1 个元素,依次存入一维数组。
下标对应公式:
设
n
∗
n
n * n
n∗n 二维矩阵
a
[
i
]
[
j
]
{a[i][j]}
a[i][j] 压缩到 一维矩阵
A
[
w
]
{A[w]}
A[w] 中,A下标从0开始,有
i f ( i < j ) s w a p ( i , j ) {if(i < j) ~~~ swap (i,j)} if(i<j) swap(i,j)
w = ∑ n − j + 2 n + ( i − j ) {w = \sum_{n-j+2}^{n} + (i-j)} w=∑n−j+2n+(i−j)
对应无向图
(2)上/下三角矩阵
定义:
以下三角为例, a [ i ] [ j ] = 0 ( j > i ) a[i][j] = 0 ~~~(j > i) a[i][j]=0 (j>i)
压缩:
以下三角矩阵为例,其压缩方法与对称矩阵完全相同。
对应有向图:
(3)三对角矩阵/带状矩阵
定义:
a [ i ] [ j ] = 0 ( ∣ i − j ∣ > 1 ) a[i][j] = 0 ~~~(~|i-j|>1~) a[i][j]=0 ( ∣i−j∣>1 )
实例:
行优先压缩:
从上到下按行将有效元素依次排列到一维数组。
下标对应关系:
设
n
∗
n
n * n
n∗n 二维矩阵
a
[
i
]
[
j
]
{a[i][j]}
a[i][j] 压缩到 一维矩阵
A
[
w
]
{A[w]}
A[w] 中,A下标从0开始,有
w
=
2
∗
i
+
j
−
3
{w = 2* i+j-3}
w=2∗i+j−3
列优先压缩:
从上到下按行将有效元素依次排列到一维数组。
下标对应关系:
设
n
∗
n
n * n
n∗n 二维矩阵
a
[
i
]
[
j
]
{a[i][j]}
a[i][j] 压缩到 一维矩阵
A
[
w
]
{A[w]}
A[w] 中,A下标从0开始,有
w
=
2
∗
j
+
i
−
3
{w = 2 *j+i-3}
w=2∗j+i−3
二.栈和队列
1.栈
(1)顺序栈
const int maxsize = 11;
struct SqStack {
int top;
int data[maxsize];
};
void init(SqStack& s) {//初始化
s.top = -1;
}
bool pop(SqStack& s,int e)//出栈
{
if (s.top == -1) {
return false;
}
e = s.data[s.top--];
return true;
}
bool push(SqStack& s, int e)//入栈
{
if (s.top == maxsize - 1) {
return false;
}
s.data[++s.top] = e;
return true;
}
bool StackEmpty(SqStack s) {//判空
return (s.top == -1);
}
bool StackFull(SqStack s) {//判满
return (s.top >= maxsize - 1);
}
(2)单链栈
typedef struct LNode {
int data;;
LNode* next;
}*LinkList;
struct LiStack {
LinkList top;
};
void init(LiStack& LS) {//初始化
LinkList L;
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
LS.top = L;
}
bool pop(LiStack& LS,int &e)//出栈
{
if (LS.top->next == NULL) {
return false;
}
e = LS.top->next->data;
LNode* s = LS.top->next;
LS.top->next = LS.top->next->next;
free(s);
return true;
}
void push(LiStack& LS, int e)//入栈
{
LNode*s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = LS.top->next;
LS.top->next = s;
}
bool StackEmpty(LiStack &LS) {//判空
return LS.top->next == NULL;
}
void StackFull(LiStack& LS) {//判满
//???
}
(3)双链栈
typedef struct DNode {
int data;
DNode* pre;
DNode* next;
}*DLinkList;
struct DLiStack {
DLinkList stack;
DNode* top;
};
void init(DLiStack& LS) {//初始化
DLinkList L;
L = (DLinkList)malloc(sizeof(DNode));
L->next = NULL;
L->pre = NULL;
LS.stack = L;
LS.top = L;
}
bool pop(DLiStack& LS,int &e)//出栈
{
if (LS.stack->next == NULL) {
return false;
}
e = LS.top->data;
DNode* s = LS.top->pre;
free(LS.top);
LS.top = s;
LS.top->next = NULL;
return true;
}
void push(DLiStack& LS, int e)//入栈
{
DNode*s = (DNode*)malloc(sizeof(DNode));
s->data = e;
s->next = NULL;
s->pre = LS.top;
LS.top->next = s;
LS.top = s;
}
bool StackEmpty(DLiStack &LS) {//判空
return LS.stack->next == NULL;
}
void StackFull(DLiStack& LS) {//判满
//???
}
2. 队列
(1)循环队列
const int maxsize = 11;
struct SqQueue{
int data[maxsize];
int front, rear;
};
void init(SqQueue& Q)//初始化
{
Q.front = Q.rear = 0;
}
int nex(int a) {
return (a + 1) % maxsize;
}
bool QueEmpty(SqQueue& Q) {
return Q.front == Q.rear;
}
bool QueFull(SqQueue& Q) {
return nex(Q.rear) == Q.front;
}
bool pop(SqQueue& Q, int &e)
{
if (QueEmpty(Q)) {
cout << "pop flase" << endl;
return false;
}
e = Q.data[Q.front];
Q.front = nex(Q.front);
return true;
}
bool push(SqQueue &Q,int e)
{
if (QueFull(Q)) {
cout << "push flase" << endl;
return false;
}
Q.data[Q.rear] = e;
Q.rear = nex(Q.rear);
return true;
}
(2)链队列
typedef struct LNode {
int data;;
LNode* next;
}*LinkList;
struct LQueue {
LNode* front;
LNode* tail;
};
void init(LQueue& LQ) {//初始化
LinkList L;
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
LQ.front = L;
LQ.tail = L;
}
bool pop(LQueue& LS,int &e)//出队
{
if (LS.front->next == NULL) {
return false;
}
e = LS.front->next->data;
LNode* s = LS.front->next;
LS.front->next = LS.front->next->next;
free(s);
return true;
}
void push(LQueue& LS, int e)//入队
{
LNode*s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = NULL;
LS.tail->next = s;
LS.tail = s;
}
bool QueqeEmpty(LQueue &LS) {//判空
return LS.front->next == NULL;
}
void QueueFull(LQueue& LS) {//判满
//???
}
三、树与二叉树
1.二叉树
(1)二叉树节点性质
- 非空二叉树上叶子结点数为度为二的结点数加一
即 n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1 - 非空二叉树第 k k k 层至多有 2 k + 1 2^{k+1} 2k+1 个结点(满层)
- 高度为 h h h 的二叉树至多有 2 h − 1 2^h-1 2h−1 个结点(满二叉树)
- 具有 n n n个结点的完全二叉树高度为 ⌈ l o g 2 ( n + 1 ) ⌉ {\lceil log_2(n+1) \rceil} ⌈log2(n+1)⌉ 或 ⌊ l o g 2 n ⌋ + 1 \lfloor log_2n \rfloor + 1 ⌊log2n⌋+1
![](https://i-blog.csdnimg.cn/blog_migrate/3bb96e47cfbfae7326fee2df26001d41.png)
(2)二叉树顺序存储
以1为根
const int MaxSize = 107;
struct SqBiTree {
int length;
int tree[MaxSize];
};
void init(SqBiTree& SBT) {
SBT.length = 0;
}
int findFather(int i) {//找父节点
return (i >> 1);
}
int leftChild(int i) {//找左儿子
return (i << 1);
}
int rightChild(int i) {//找右儿子
return (i << 1 | 1);
}
bool CrossJudeg(SqBiTree SBT,int x) {//越界判断
return x <= SBT.length;
}
int out[MaxSize + 1], len = 0;
void PreOrder(SqBiTree SBT,int x) {//前序遍历
if (!CrossJudeg(SBT, x)) {
return;
}
out[++len] = SBT.tree[x];
PreOrder(SBT, leftChild(x));
PreOrder(SBT, rightChild(x));
}
void InOrder(SqBiTree SBT, int x) {//中序遍历
if (!CrossJudeg(SBT, x)) {
return;
}
InOrder(SBT, leftChild(x));
out[++len] = SBT.tree[x];
InOrder(SBT, rightChild(x));
}
void PostOrder(SqBiTree SBT, int x) {//后序遍历
if (!CrossJudeg(SBT, x)) {
return;
}
PostOrder(SBT, leftChild(x));
PostOrder(SBT, rightChild(x));
out[++len] = SBT.tree[x];
}
以0为根
int findFather(int i) {//找父节点
return ((i - 1) >> 1);
}
int leftChild(int i) {//找左儿子
return ((i << 1) + 1);
}
int rightChild(int i) {//找右儿子
return ((i << 1) + 2);
}
bool CrossJudeg(SqBiTree SBT,int x) {//越界判断
return x < SBT.length;
}
其他一样
2.树
【1】树的性质
- 树中的结点数等于所有结点的度数之和加1。
- 度为m的树第i层上至多有 m i − 1 m^i-1 mi−1个结点
- 度为
m
m
m的树、
m
m
m叉树的区别
【2】树的存储
(1)双亲表示法
const int MaxSize = 107;
struct PTNode {
int data;
int father; //父节点
};
struct PTree {
int n; //结点数
PTNode tree[MaxSize];
};
(2)孩子表示法
const int MaxSize = 107;
typedef struct LiNode {
int x;
LiNode* next;
}*LinkList;
typedef struct CTNode {
int data;
LinkList sons;
};
struct CTree {
int n; //结点数
int root; //根编号
CTNode tree[MaxSize];
};
[注]
树的孩子表示法、图的邻接表存储、散列表的拉链法和基数排序都是通过多个顺序存储的链表实现的。
(3)孩子兄弟表示法
typedef struct CSNode {
int data;
CSNode* FirsrChild;
CSNode* RightBrother;
}*CSTree;
(4)实例
[1] 三叉树
![](https://i-blog.csdnimg.cn/blog_migrate/34ea5249940ba0ba24ac46ef09261e0d.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/2cd5e5283b5ab4a70698da0172e0616d.jpeg)
[2] 森林
3.哈夫曼树
【1】实例
有符号与其对应权值如下,构建哈夫曼树
符号 | 权值(%) |
---|---|
a | 31 |
b | 16 |
c | 10 |
d | 8 |
e | 11 |
f | 20 |
g | 4 |
【2】合并步骤
文字描述:所有树(结点也算)按根结点权值排成一优先队列,每次在队列中选择根节点权值最小的两个树(结点),将其作为左右子树合并为一个新的二叉树,新树根节点权值即为两子树根节点权值之和,将新树再次放入优先队列继续参与合并,直到队列中只剩一颗完整的二叉树,即是要求的哈夫曼树。
【3】哈夫曼编码
以合成的哈夫曼树为基准,按照“向左为0,向右为1”的方式,描述从根节点向叶结点的路径,即可得到当前叶结点的哈夫曼编码。
符号 | 哈夫曼编码 |
---|---|
a | 11 |
b | 101 |
c | 010 |
d | 1001 |
e | 011 |
f | 00 |
g | 1000 |
【4】解码
直接对码字进行前缀匹配,匹配对应的编码表,若有前缀匹配上,即可翻译出其对应信息,然后截取下匹配的部分后继续对剩余码字进行前缀匹配,直到码字全部截取匹配完毕。由于其前缀编码的特性,这种截取方式是唯一的。
4.并查集
【1】定义(代码)
const int MaxSize = 107;
int father[maxn]; //集合元素数组
void init() //初始化
{
for (int i = 1; i <= MaxSize; i++) {
father[i] = i;
}
}
int Find(int a) //路径压缩查找
{
if (father[a] == a) {
return a;
}
return father[a] = Find(father[a]);
}
void Union(int a, int b) //合并
{
int x = Find(a);
int y = Find(b);
father[x] = y;
}
【2】实例
构建
![](https://i-blog.csdnimg.cn/blog_migrate/47ceefdc13659b44af9277e85c02088a.jpeg)
路径压缩
![](https://i-blog.csdnimg.cn/blog_migrate/83c07ed37c27e2b61901139361d87de3.jpeg)
5.二叉排序树
按照一定规则对插入数据进行排序的二叉树(多为左小右大)
【1】插入
按照规则添加叶结点
实例
【2】查找
按规则从根查找
实例
A
S
L
成功
=
(
1
+
2
∗
2
+
3
∗
4
+
4
∗
2
+
5
)
/
10
=
3
{ASL_{成功} = (1 + 2*2+3*4+4*2+5) ~/ ~10 = 3 }
ASL成功=(1+2∗2+3∗4+4∗2+5) / 10=3
A
S
L
失败
=
(
6
∗
4
+
3
∗
5
+
2
∗
6
)
/
10
=
5.1
{ASL_{失败} = ~~~~~~~(6*4+3*5+2*6) ~~~~~~~/~10= 5.1}
ASL失败= (6∗4+3∗5+2∗6) / 10=5.1
【3】删除
①删除叶子结点:直接删除
②删除只有左子树或只有右子树的结点:让其子树的根节点(即被删除结点的左右子树)代替
③删除有左右子树的结点:根据中序遍历的特性,替换成直接前驱或直接后继
法Ⅰ:替换成直接后继。找到其右子树的最左下的结点替换(最左下即该结点一定没有左孩子),而被替换的结点由它的右子树的根节点代替(转换成②)
法Ⅱ:替换成直接前驱。找到其左子树的最右下的结点替换(最右下即该结点一定没有右孩子),而被替换的结点由它的左子树的根节点代替(转换成②)
排序二叉树删除叶子结点后重新加入:不会改变树形结构
排序二叉树删除分支结点后重新加入:一定改变树形结构
![](https://i-blog.csdnimg.cn/blog_migrate/1f603d0cfc61bd754344e58264c009dd.jpeg)
实例
6.平衡二叉树
【1】构建
2 种「旋转」方式 :
左旋:
- 旧根节点为新根节点的左子树
- 新根节点的左子树(如果存在)为旧根节点的右子树
右旋 :
- 旧根节点为新根节点的右子树
- 新根节点的右子树(如果存在)为旧根节点的左子树
- LL 型:插入左孩子的左子树,右旋
- RR 型:插入右孩子的右子树,左旋
- LR 型:插入左孩子的右子树,子树先左旋,整体再右旋
- RL 型:插入右孩子的左子树,子树先右旋,整体再左旋
实例
【3】查找
A
S
L
成功
=
1
+
2
∗
2
+
4
∗
3
+
2
∗
4
)
/
9
=
2.78
{ASL_{成功} = ~1+2*2+4*3+2*4) ~ / 9 ~=~ 2.78}
ASL成功= 1+2∗2+4∗3+2∗4) /9 = 2.78
A
S
L
失败
=
(
6
∗
4
+
4
∗
5
)
/
10
=
4.4
{ASL_{失败} = ~~~~~~~~~(6*4+4*5) ~~~~~~~~~/~10= 4.4}
ASL失败= (6∗4+4∗5) / 10=4.4
四、图
1.图的性质
【1】无向图
全部度数和
=
2
∗
边数
{全部度数和= 2 * 边数}
全部度数和=2∗边数
连通性: 不连通图最多具有
n
−
1
{n-1}
n−1 个顶点
【2】有向图
入度
=
出度
=
边数
{入度= 出度 = 边数}
入度=出度=边数
连通性: 强连通图需要最少的边数——至少需要
n
n
n 条边构成一个环路
2.图的存储
【1】定义
(1)顺序存储
const int MaxVexSize = 107; //最大点数
typedef char VexType; //点类型
typedef int EdgeType; //边类型
struct MGraph {
VexType Vex[MaxVexSize]; //点集
EdgeType Edge[MaxVexSize][MaxVexSize]; //邻接矩阵
int VexNum, EdgeNum; //点数、边数
};
(2)链式存储
const int MaxVexSize = 107; //最大点数
typedef char VexType; //点类型
typedef int EdgeType; //边类型
struct ArcNode { //边表结点
int adjvex; //指向顶点
ArcNode* next; //下一条边
EdgeType info; //边权
};
typedef struct VNode { //顶点表结点
VexType data; //点权
ArcNode* first; //指向边表首条边
}AdjList[MaxVexSize];
struct ALGarph {
AdjList Vexs; //邻接表
int VexNum, EdgeNum; //顶点数、边数
};
【2】实例
![](https://i-blog.csdnimg.cn/blog_migrate/cfb4530de8c6cc1d7d8eda9d6374d863.jpeg)
(1)无向图
邻接矩阵
邻接表
(2)有向图
邻接矩阵
、
邻接表
3.最小生成树
(1)Prim
每次选择当前生成树可到达的还没进树的最近点
(2)Kruskal
每次选择未进树的最短的边
4.最短路
【1】Dijkstra
(1)实例
(2)文字描述
step1. 以①为起点,可到达<2,5><3,8><6,3>,选择其最近的顶点⑥
step2. 加入⑥后,可到达<2,5><3,8><4,9><5,4>,选择于其最近的顶点⑤
step3. 加入⑤后,可到达<2,5><3,8><4,9>,选择于其最近的顶点②
step4. 加入②后,可到达<3,8><4,9>,选择于其最近的顶点③
step5. 加入③后,可到达<4,9>,选择于其最近的顶点③
【2】BFS求解非带权图最短路
过程:
从源点开始对非带权图进行BFS,队列中初始只有源点,记录距离为0。每出队一个顶点,遍历其所邻接的还没遍历过的其他顶点,记录源点到这些顶点距离为当前出队顶点距离加一,加入队列。直到队列为空遍历完毕。
5.拓扑排序
(1)流程
每次排序,遍历剩余点,找到一个入度为零的点放入拓扑序列,并将其指向的各个顶点入度减一,一直到全部排完。如果还有剩余点但是找不到入度为零的顶点,说明有环存在。
(2)实例
![](https://i-blog.csdnimg.cn/blog_migrate/917bd9d25dac2b6f39a05d603e06bb18.jpeg)
邻接矩阵
合法序列
-
① ② ③ ④ ⑤ ⑥
-
① ② ③ ⑤ ④ ⑥
-
① ② ⑤ ③ ④ ⑥
-
① ③ ② ④ ⑤ ⑥
-
① ③ ② ⑤ ④ ⑥
6.关键路径
(1)步骤
- 求所有事件(顶点)的最早发生时间 v e ve ve 【正推】
- 求所有事件的最晚发生时间 v v vl 【反推】
- 求所有活动(边)的最早发生时间 e e e 【正推】
- 求所有活动的最晚发生时间 l l l 【反推】
- 求所有活动的时间余量 d = l − e {d = l - e} d=l−e,为零者构成关键路径,由源点到汇点构成的最长路径即为关键路径长度
(2)实例
关键路径: ① ③ ④ ⑥
关键路径长度: 8
(2)关键路径长度的现实意义
简单说,表示整个工程最少需要多少时间(成本)来完成。
1) 关键路径上的所有活动都是关键活动,它是决定整个工程的关键因素,因此可通过加快关键活动来缩短整个工程的工期。但也不能任意缩短关键活动,因为一旦缩短到一定的程度,该关键活动就可能会变成非关键活动。
2) 网中的关键路径并不唯一,且对于有几条关键路径的网,只提高一条关键路径上的关键活动速度并不能缩短整个工程的工期,只有加快那些包括在所有关键路径上的关键活动才能达到缩短工期的目的。
五、查找
1.分块查找
实例
顺序查找索引
A
S
L
成功
=
(
2
+
3
+
4
+
5
+
6
+
7
+
3
+
4
+
5
+
4
+
5
+
6
+
5
+
6
)
/
14
=
4.64
{ASL_{成功} = (2+3+4+5+6+7+3+4+5+4+5+6+5+6) /14 = 4.64}
ASL成功=(2+3+4+5+6+7+3+4+5+4+5+6+5+6)/14=4.64
A
S
L
失败
=
(
7
+
5
+
6
+
6
+
4
)
/
5
=
5.6
{ASL_{失败} = (7+5+6+6+4)/5 = 5.6}
ASL失败=(7+5+6+6+4)/5=5.6
折半查找索引
A
S
L
成功
=
(
3
+
4
+
5
+
6
+
7
+
8
+
4
+
5
+
6
+
3
+
4
+
5
+
4
+
5
)
/
14
=
4.93
{ASL_{成功} = (3+4+5+6+7+8+4+5+6+3+4+5+4+5) /14 = 4.93}
ASL成功=(3+4+5+6+7+8+4+5+6+3+4+5+4+5)/14=4.93
A
S
L
失败
=
(
8
+
6
+
5
+
5
+
4
)
/
5
=
5.6
{ASL_{失败} = (8+6+5+5+4)/5 = 5.6}
ASL失败=(8+6+5+5+4)/5=5.6
2.折半查找
实例
序列: 7 10 13 16 19 29 32 33 37 41 43
查找分析树:
![](https://i-blog.csdnimg.cn/blog_migrate/583e60096ef4ec405d3a65d5eaf8880f.jpeg)
ASL:
A
S
L
成功
=
(
1
∗
1
+
2
∗
2
+
3
∗
4
+
4
∗
4
)
/
11
=
3
{ASL_{成功} = (1*1+2*2+3*4+4*4)/11=3}
ASL成功=(1∗1+2∗2+3∗4+4∗4)/11=3
A
S
L
失败
=
(
3
∗
4
+
4
∗
8
)
/
12
=
3.67
{ASL_{失败} = (3*4+4*8)/12 = 3.67}
ASL失败=(3∗4+4∗8)/12=3.67
3.散列查找
实例
序列: 19,14,23,01,68,20,84,27,55,11,10,79
(1)线性探测法
ASL:
A
S
L
成功
=
(
1
∗
6
+
2
+
3
∗
3
+
4
+
9
)
/
12
=
2.5
{ASL_{成功} = (1*6+2+3*3+4+9)/12=2.5}
ASL成功=(1∗6+2+3∗3+4+9)/12=2.5
A
S
L
失败
=
(
12
+
11
+
10
+
9
+
8
+
7
+
6
+
5
+
4
+
3
+
2
+
1
)
/
13
=
6
{ASL_{失败} = (12+11+10+9+8+7+6+5+4+3+2+1)/13=6}
ASL失败=(12+11+10+9+8+7+6+5+4+3+2+1)/13=6
(2)拉链法
![](https://i-blog.csdnimg.cn/blog_migrate/db73aa7bdeb179059701f1b86b520538.jpeg)
ASL:
A
S
L
成功
=
(
1
∗
6
+
2
∗
4
+
3
+
4
)
/
12
=
1.75
{ASL_{成功} = (1*6+2*4+3+4)/12=1.75}
ASL成功=(1∗6+2∗4+3+4)/12=1.75
A
S
L
失败
=
(
1
∗
2
+
2
∗
3
+
4
)
/
13
=
0.92
{ASL_{失败} = (1*2+2*3+4)/13=0.92}
ASL失败=(1∗2+2∗3+4)/13=0.92
六、排序
1.希尔排序
【1】基本思想
先将待排序表分割成若干形如 L [ i , i + d , i + 2 d , … … , i + k d ] L[i,i+d,i+2d,……,i+kd] L[i,i+d,i+2d,……,i+kd]的“特殊”子表,即把相隔某个“增量”的记录组成一个子表,对各个子表分别进行直接插入排序,当整个表中的元素已呈“基本有序”时,再对全体记录进行一次直接插入排序。
【2】实例
![](https://i-blog.csdnimg.cn/blog_migrate/733dfe4ffce4607478a087f0fa8bfc76.jpeg)
【3】复杂度和稳定性
时间复杂度:
最好
/
平均:
O
(
n
1.3
)
,最坏:
O
(
n
2
)
{最好/平均:O(n^{1.3}),最坏:O(n^2)}
最好/平均:O(n1.3),最坏:O(n2)
空间复杂度:
O
(
1
)
{O(1)}
O(1)
稳定性: 不稳定
2.堆排序
【1】基本思想
首先将存放在
L
[
1
…
n
]
L[1…n]
L[1…n]中的n个元素建成初始堆,由于堆本身的特点(以大顶堆为例),堆顶元素就是最大值。输出堆顶元素后,通常将堆底元素送入堆顶,此时根结点已
不满足大顶堆的性质,堆被破坏,将堆顶元素向下调整使其继续保持大顶堆的性质,再输出堆顶元素。如此重复,直到堆中仅剩一个元素为止。
【2】实例
![](https://i-blog.csdnimg.cn/blog_migrate/66703f1900aa688423a9b16b7f4c9c9b.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/647b31ffcfc6c0836cef48cf6b96c26d.jpeg)
【3】复杂度和稳定性
时间复杂度:
最好
/
最坏
/
平均:
O
(
n
l
o
g
2
n
)
{最好/最坏/平均:O(nlog_2n)}
最好/最坏/平均:O(nlog2n)
空间复杂度:
O
(
1
)
{O(1)}
O(1)
稳定性: 不稳定
3.快速排序
【1】基本思想
基于分治法的思想:在待排序表 L [ 1 … n ] L[1…n] L[1…n] 中任取一个元素pivot作为枢轴(或基准,通常取首元素),通过一趟排序将待排序表划分为独立的两部分 L [ 1 … k − 1 ]和 L [ k + 1 … n ] L[1…k-1]和L[k+1…n] L[1…k−1]和L[k+1…n],使得 L [ 1.. k − 1 ] L[1..k-1] L[1..k−1]中的所有元素小于pivot, L [ k + 1.. n ] L[k+1..n] L[k+1..n] 中的所有元素大于等于pivot,则pivot放在了其最终位置 L ( k ) L(k) L(k)上,这个过程称为一趟快速排序(或一次划分)。然后分别递归地对两个子表重复上述过程,直至每部分内只有一个元素或空为止,即所有元素放在了其最终位置上。
【2】实例
![](https://i-blog.csdnimg.cn/blog_migrate/3b891803462a0ce2ae2e3dea58e87f77.png)
【3】复杂度和稳定性
时间复杂度:
最好
/
平均:
O
(
n
l
o
g
2
n
)
)
,最坏:
O
(
n
2
)
{最好/平均:O(nlog_2n)),最坏:O(n^2)}
最好/平均:O(nlog2n)),最坏:O(n2) (内部排序平均性能最优)
空间复杂度:
平均:
O
(
l
o
g
2
n
)
,最坏:
O
(
n
)
{平均:O(log_2n) ,最坏:O(n)}
平均:O(log2n),最坏:O(n)
稳定性: 不稳定
4.基数排序
【1】流程
下面描述以r为基数的最低位优先基数排序的过程,在排序过程中,使用 r r r 个队列 Q 0 , Q 1 , … , Q r − 1 {Q_0,Q_1,…,Q_{r-1}} Q0,Q1,…,Qr−1 ,基数排序的过程如下:
对 i = 0 , 1 , … , d − 1 i=0,1,…,d-1 i=0,1,…,d−1 ,依次做一次“分配”和“收集”(其实是一次稳定的排序过程)。
分配: 开始时,把 Q 0 , Q 1 , … , Q r − 1 {Q_0,Q_1,…,Q_{r-1}} Q0,Q1,…,Qr−1 各个队列置成空队列,然后依次考察线性表中的每个结点 a j ( j = 0 , 1 , … , n − 1 ) {a_j(j=0,1,…,n-1)} aj(j=0,1,…,n−1),若 a j a_j aj 的关键字 k j i = k {k^{i}_{j}=k} kji=k,就把 a j a_j aj 放进 Q k Q_k Qk 队列中。
收集: 把
Q
0
,
Q
1
,
…
,
Q
r
−
1
{Q_0,Q_1,…,Q_{r-1}}
Q0,Q1,…,Qr−1 各个队列中的结点依次首尾相接,得到新的结点序列,从而组成新
的线性表。
【2】实例
第一趟:
第二趟:
【3】复杂度和稳定性
时间复杂度:
O
(
d
(
n
+
r
)
)
{O(d(n+r))}
O(d(n+r)) (
d
d
d 趟分配和收集,与序列初始状态无关)
空间复杂度:
O
(
r
)
{O(r)}
O(r)
稳定性: 稳定