数据结构基础代码总结(树和图)
树和图部分
# 一、树和二叉树
# 树的遍历
1 |
|
# 线索二叉树
1 |
|
# 遍历
1 | //中序线索树的遍历 |
# 二、图
# BFS
广度优先搜索
主要使用队列实现,对每个节点可能到达的路径进行入队出队判断
1 |
|
# DFS
深度优先搜索
1 | bool visited[MAX_VERTEX_NUM]; |
# 最小生成树
关于图的几个概念定义:
连通图:在无向图中,若任意两个顶点 vivi 与 vjvj 都有路径相通,则称该无向图为连通图。
强连通图:在有向图中,若任意两个顶点 vivi 与 vjvj 都有路径相通,则称该有向图为强连通图。
连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。
生成树:一个连通图的生成树是指一个连通子图,它含有图中全部 n 个顶点,但只有足以构成一棵树的 n-1 条边。一颗有 n 个顶点的生成树有且仅有 n-1 条边,如果生成树中再添加一条边,则必定成环。
最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。
下面介绍两种求最小生成树算法
1.Kruskal 算法
此算法可以称为 “加边法”,初始最小生成树边数为 0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。
把图中的所有边按代价从小到大排序;
把图中的 n 个顶点看成独立的 n 棵树组成的森林;
按权值从小到大选择边,所选的边连接的两个顶点 ui,viui,vi, 应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。
重复 (3), 直到所有顶点都在一颗树内或者有 n-1 条边为止。
- Prim 算法
此算法可以称为 “加点法”,每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点 s 开始,逐渐长大覆盖整个连通网的所有顶点。
图的所有顶点集合为 VV;初始令集合 u={s},v=V−uu={s},v=V−u;
在两个集合 u,vu,v 能够组成的边中,选择一条代价最小的边 (u0,v0)(u0,v0),加入到最小生成树中,并把 v0v0 并入到集合 u 中。
重复上述步骤,直到最小生成树有 n-1 条边或者 n 个顶点为止。
由于不断向集合 u 中加点,所以最小代价边必须同步更新;需要建立一个辅助数组 closedge, 用来维护集合 v 中每个顶点与集合 u 中最小代价边信息,:
1 | struct |
# Prim 算法
最小生成树是一个图的极小连通子图,它包含原图的所有顶点,并且所有边的权值之和尽可能小。
Prim 算法就是图的最小生成树算法之一,Prim 算法是一种求解加权无向连通图的 MST 问题的贪心算法。它能找出一个边的子集,使得其构成的树包含图中所有顶点,且边的权值之和最小。
Prim 算法以图的顶点为基础,从首个初始顶点,寻找到达其他顶点权值最小的边,并把该顶点加入到 “ 已到达顶点
” 的集合中,此时,这个集合就是这个图的最小生成树。
一般用一维数组比较方便表达最小生成树,数组下标所对应的元素,代表该顶点在最小生成树当中的父亲节点。
1 | // 基于Prim算法实现最小生成树 |
实验结果
请输入带权无向图的定点数和边数 (以空格隔开):
7 12
请依次输入 12 条边的开始节点,结束节点,权值 (以空格隔开):
1 2 23
1 6 28
1 7 36
2 3 20
2 7 1
3 4 15
3 7 4
4 5 3
4 7 9
5 6 17
5 7 16
6 7 25
lowcost [] 数组:0 23 4 9 3 17 1
closest [] 数组:0 1 7 7 4 5 2
最小生成树总的花费是:57
D:\projects\test\x64\Release\test.exe (进程 1788) 已退出,返回代码为: 0。
# Kruskal 算法
1 | /************************************************************************ |
# Dijkstra 算法 (求单源最短路径问题)
# 算法原理
- 适合求解有回路的带权图的最短路径
- 可以求任意两个顶点的最短路径
- 不适合求带负权值的最短路径问题
# 邻接矩阵实现
1 |
|
结果:
参考文章
# 邻接表实现
待定
# Floyd 算法 (求多源最短路径问题)
# 算法思想
概括为迭代更新 i 经由 k 到 j 的最短路径.
# 算法原理
- 从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
- 对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比已知的路径更短。如果是更新它。
- 把图用邻接矩阵 G 表示出来,如果从 Vi 到 Vj 有路可达,则 G [i][j]=d,d 表示该路的长度;否则 G [i][j]= 无穷大。定义一个矩阵 D 用来记录所插入点的信息,D [i][j] 表示从 Vi 到 Vj 需要经过的点,初始化 D [i][j]=j。把各个顶点插入图中,比较插点后的距离与原来的距离,G [i][j]= min ( G [i][j], G [i][k]+G [k][j] ),如果 G [i][j] 的值变小,则 D [i][j]=k。在 G 中包含有两点之间最短道路的信息,而在 D 中则包含了最短通路径的信息。
- 比如,要寻找从 V5 到 V1 的路径。根据 D,假如 D (5,1)=3 则说明从 V5 到 V1 经过 V3,路径为 {V5,V3,V1},如果 D (5,3)=3,说明 V5 与 V3 直接相连,如果 D (3,1)=1,说明 V3 与 V1 直接相连。
# 邻接矩阵实现
1 |
|
# 拓扑排序
# 原理
- 从 AOV 网中选择一个没有前驱的顶点并输出.
- 从网中删除该顶点和所有以它为起点的有向边.
- 重复直至 AOV 网为空或当前网中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环.
# 栈实现拓扑排序(邻接表实现)
1 |
|
测试数据,新建 in.txt 文件输入内容
1)有环
4 4
1 2
2 3
3 4
4 2
2)无环
12 16
1 2
1 3
2 3
1 4
3 5
4 5
11 6
5 7
3 7
3 8
6 8
9 10
9 11
9 12
10 12
1 12
结果: