线性表
顺序表
定义
1 |
|
逆置
1 | //逆置 |
删除
1 | //删除所有介于s和t之间的结点 |
逆置的应用
1 | //将a1,a2...am,b1,b2...bn交换为序列b1,b2..bn,a1,a2...am |
链表
定义
1 |
|
初始化
1 | //创造一个空的线性表 |
查找
1 | //从第一个位置起查找与e匹配的数据元素,若存在则返回该数据元素的位置 |
插入
1 | //在单链表的第i个数据元素之前插入新的数据元素 |
删除
1 | //在单链表中删除第i个数据元素并用数据变量e返回其值 |
栈和队列
栈
1 |
|
队列
1 |
|
stack&queue 头文件
1 |
|
树
二叉链表
定义
1 |
|
遍历
先序遍历
1 | //先序遍历二叉树(递归) |
中序遍历
1 | //中序遍历二叉树(递归) |
后序遍历
1 | //后序遍历二叉树(递归) |
层序遍历
1 | //层序遍历 |
深度
1 | //求二叉树的深度(递归) |
构建二叉树
先序 + 中序
1 | //按先序和中序次序建立二叉树 |
中序 + 后序
1 | BTNode* CreateBinaryTree4(Elemtype_t ch1[],Elemtype_t ch2[],int postL,int postR, |
中序 + 层序
1 | //中序+层序 |
打印 x 的祖先
1 | //打印结点值为x的所有祖先节点 |
查找指定结点
1 | //返回二叉树中元素值为e的结点指针 |
其他操作
交换二叉树的左右节点
1 | //交换二叉树的左右节点 |
判断完全二叉树
1 | //判断二叉树是否是完全二叉树 |
复制一颗二叉树
1 | //复制一颗二叉树 |
统计叶结点个数
1 | //统计树的叶结点个数 |
二叉搜索树 (BST)
查找
1 | //二叉搜索树动态查找 |
删除
1 | //删除 |
平衡二叉树 (AVL)
平衡二叉树的插入
1 |
|
其他操作
判断是否为平衡二叉树
1 | //暴力解法是对每个结点计算左右子树高度求平衡因子 |
堆 (优先队列)
大根堆的初始化
1 | struct HNode{ |
大根堆的插入
1 | //大根堆的插入 |
大根堆删除最大值
1 | //大根堆删除最大值 |
二叉树调整为最大堆
1 | //二叉树调整为最大堆 |
哈夫曼树
1 | typedef struct HTNode *HuffmanTree; |
图
图的存储
邻接矩阵
1 |
|
邻接表
1 | //通用版基础定义方法 |
1 | //利用vector快速构建 |
十字链表
1 |
|
* 邻接多重表
图的遍历
1 | const int MAXV=1000; |
DFS
- 伪代码描述
1 | void DFS(int u){ //u为起点 |
- 邻接矩阵
1 | int n,G[MAXV][MAXV]{INF}; //n为当前图的顶点总数 |
- 邻接表
1 | vector <int> Adj[MAXV]; //和定义中不一样的是,这里压入的数表示其相邻的结点 |
- 遍历函数
1 | void BFSTrave(){ //这个函数是必要的,因为图不一定是连通的,所以一定要判断所有的顶点 |
BFS
- 伪代码描述
1 | void BFS(int u){ //u为起点 |
- 邻接矩阵
1 | int n,G[MAXV][MAXV]; |
- 邻接表
1 | vector <int> G[MAXV]; |
- 遍历函数
1 | void BFSTrave(){ |
最短路径
1 | const int MAXV 1000; //最大顶点数 |
Dijkstra
- 伪代码描述
1 | Dijkstra(G,d[],s){ //G为图,d为源点,s为起点 |
- 邻接矩阵
1 | int n,G[MAXV][MAXV]; |
- 邻接表
1 | struct Node { |
一些改进
- 保存每个节点最短路径的前驱节点并用递归的方法输出最短路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//在更新每个节点的最短距离(d[i])时记录前驱
//临界矩阵的部分调整
pre[MAXV] //保存每个节点的前驱节点
for(int v=0;v
if(vis[v]==flase&&G[u][v]!=INF&&d[u]+G[u][v]
d[v]=d[u]+G[u][v];
pre[v]=u;
}
}
//此时可以递归的输出最短路径
void DFS(int s,int v){ //s为起点,v为当前节点
if(s==v){
printf("%d\n",s);
return;
}
DFS(s,pre[v]);
printf("%d\n",v);
}多条最短路径问题
同上只需在更新节点最短路径的时候略作调整进行相应的判断
可以新增一个数组来表示边权、点权和最短路径条数
- 给每条边在增加一条边权(二维数组 cost 记录边权,c [] 记录最小花费)
1
2
3
4
5
6
7
8
9
10
11
12for(int v=0;v
if(vis[v]==false&&G[u][v]!=INF){
if(d[u]+G[u][v]
d[v]=d[u]+G[u][v];
c[v]=c[u]+cost[u][v];
pre[v]=u;
}else if(d[u]+G[u][v]==d[v]&&c[u]+cost[u][v]
c[v]=c[u]+cost[u][v];
pre[v]=u;
}
}
}- 给每个顶点增加一个点权(weight [] 记录点权,w [] 为路径上的最大点权和)
1
2
3
4
5
6
7
8
9
10
11
12for(int v=0;v
if(vis[v]==false&&G[u][v]!=INF){
if(d[u]+G[u][v]
d[v]=d[u]+G[u][v];
w[v]=w[u]+weight[v];
pre[u]=v;
}else if(d[u]+G[u][v]==d[v]&&w[u]+weight[v]>w[v]){
w[v]=w[u]+weight[v];
pre[v]=u;
}
}
}- 到达每个顶点的最短路径条数(用 num [] 保存)
1
2
3
4
5
6
7
8
9
10for(int v=0;v
if(vis[v]==false&&G[u][v]!=INF){
if(d[u]+G[u][v]
d[v]=d[u]+G[u][v];
num[v]=num[u];
}else if(d[u]+G[u][v]==d[v]){
num[v]+=num[u];
}
}
}
Bellman-Ford
伪代码描述
1
2
3
4
5
6
7
8
9
10
11
12for(i=0;i
-1 ;i++){ //进n-1轮操作,其中n为顶点数
for(u的所有邻接边u->v){
if(d[u]+length[u->v]
d[v]=d[u]+length[u->v]
}
}
//如果图中有负环,则d还能进行更新,返回false
for(u的所有邻接边u->v){
if(d[u]+length[u->v]
return false;
return true;
}邻接表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28struct Node{
int v,dis;
}
vectorG[MAXV]
int n;
int d[MAXV];
bool Bellman(int s){
fill(d,d+MAXV,INF);
d[s]=0;
for(int i=0;i-1 ;i++){
for(int u=0;u
for(int j=0;j
int v=G[u][j].v;
int dis=G[u][j].dis;
if(d[u]+dis
d[v]=d[u]+dis;
}
}
}
for(int u=0;u
for(int j=0;j
int v=G[u][j].v;
int dis=G[u][j].dis;
if(d[u]+dis
return false;
}
}
SPFA
伪代码描述
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22bool SPFA(G,d[],s){
int Queue[maxn];
int front=rear=0;
int inq[maxn];
初始化inq、num、d;
源点s入队;
while(队列非空){
取出队首元素u;
for(u的所有邻接边u->v){
if(d[u]+dis
d[v]=d[u]+dis;
if(v当前不在队列){
v入队;
if(v入队次数大于n-1){
return;//有可达负环
}
}
}
}
}
return;//达到最优
}邻接表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34vector
Adj[MAXN];
int n,num[MAXN],d[MAXN];
bool inq[MAXN];
bool SPFA(int s){
fill(d,d+MAXN,INF);
fill(inq,inq+MAXN,false);
fill(num,num+MAXN,0);
queue <int> Q;
Q.push(s);
inq[s] = true;
num[s]++;
d[s] = 0;
while(!Q.empty()){
int u = Q.front();
Q.pop();
inq[u] = false;
for(int i=0;i
int v = Adj[u][i].v;
int dis = Adj[u][i].dis;
if(d[u]+dis
d[v] = d[u]+dis;
if(!inq[v]){
Q.push(v);
inq[v] = true;
num[v]++;
if(num[v]>n-1)
return false; //进入队列n次,说明有负环
}
}
}
}
return true;
}
Floyd
外代码描述
1
2
3
4枚举所有顶点k
枚举所有以k为中介点的顶底对i和j
if(dis[i][k]+dis[k][j]
更新dis[i][j]邻接矩阵
1
2
3
4
5
6
7
8
9
10
11//Floyd解决全源最短路径问题
int n,m;
int dis[MAXV][MAXV]
void Floyd(){
for(int k=0;k
for(int i=0;i
for(int j=0;j
if(dis[i][k]!=INF&&dis[k][j]!=INF&&dis[i][k]+dis[k][j]
dis[i][j]=dis[i][k]+dis[k][j];
}
最小生成树
Prim
伪代码描述
1
2
3
4
5
6
7
8
9
10
11
12Dijkstra(G,d[],s){ //G为图,d为源点,s为起点
初始化
for(循环n次){
u = d[u]中最小且未被访问过的点;
将u标记为已经被访问;
将d[u]加入最小边权和;
for(从u出发能到达的所有顶点v){
if(v未被访问并且以u为中介使d[v]更优)
更新d[v] //这里的更新与最优与Dijkstra中是不一样的,但整个算法是一致的
}
}
}邻接矩阵
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23int n,G[MAXV][MAXV];
int d[MAXV];
bool vis[MAXV]={false};
int dijkstra(int s){
int ans;
fill(d,d+MAXV,INF);
d[s]=0;
for(int i=0;i
int u=-1,MIN=INF;
for(int j=0;j
if(vis[j]==false&&d[j]
u=j;
MIN=d[j];
}
if(u==-1) return; //这里是不连通的情况
vis[u]=true;
ans += d[u];
for(int v=0;v
if(vis[v]==flase&&G[u][v]!=INF&&G[u][v]
d[v]=G[u][v] //这就是前面说的不一样的地方
}
}
Kruskal
伪代码描述
1
2
3
4
5
6
7
8
9
10int Kruskal(){
for(从小到大枚举所有的边){
if(当前测试边的顶点在不同的两个连接块中){
将该测试边加入最小生成树;
ans+=此边权值;
最小生成树的边数加1;
当边数为n-1是结束循环;
}
}
}并查集实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45struct Edge{
int u,v;//边的两个端点编号
int cost;//边权
}E[maxn];
int father[maxn];//并查集数组
int FindFather(int x){
int z,a=x;
while(x!=father[x]){
x=father[x];
}
//路径压缩
while(a!=father[a]){
z=a;
a=father[a];
father[z]=x;
}
return x;
}
int Kruskal(int n,int m){//参数n为顶点个数,m为图的边数,函数返回边权之和
int ans=0,NumEdge=0;//ans为所求边权之和,NumEdge为当前生成树的边数
int i;
int faU,faV;
//查并集初始化
for(i=1;i<=n;i++){< span>
father[i]=i;
}
//按边权大小排序
for(i=0;i
faU=findFather(E[i].u);
faV=findFather(E[i].v);//查询测试边的两个端点所在集合的根节点
if(faU!=faV){//如果不在一个集合内
father[faU]=faV;//合并集合,把测试边加入最小生成树中
ans+=E[i].cost;//边权之和计算
NumEdge++;//当前生成树的边数加一
if(NumEdge==n-1){
break;//边数等于顶点数减一时结束算法
}
}
}
if(NumEdge!=n-1){
return -1;//无法连通时返回-1
}else{
return ans;//返回最小生成树的边权之和
}
}
拓扑排序
邻接表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26vector<int> G[MAXN];
int n,m,inDegree[MAXN];
bool topologicalSort(){
int num = 0;
queue<int> q;
for(int i=0;i
if(inDegree[i] == 0){
q.push(i);
}
}
while(!q.empty()){
int u = q.front();
q.pop();
for(int i=0;i
int v = G[u][i];
inDereee[v]--;
if(inDegree[v] == 0){
q.push(v);
}
}
G[u].clear();
num++;
}
if(num == n) return true;
else return false;
}
* 关键路径
查找
顺序查找
1 | int SeqSearch(SeqList L,Elemtype key){ |
折半查找
1 | int BinarySearch(SeqList L,Elemtype key){ |
排序
插入排序
直接插入排序
1 | void InsertSort(Elemtype A[],int n){ |
折半插入排序
1 | //折半插入排序 |
希尔排序
1 | //希尔插入排序 |
交换排序
冒泡排序
1 | //冒泡排序 |
快速排序
1 | //快速排序 |
选择排序
简单选择排序
1 | //简单选择排序 |
堆排序
1 | //堆排序 |
归并排序
二路归并排序
1 | //归并排序 |