数据结构最小生成树

合集下载

数据结构(Java版)图2(最小生成树)

数据结构(Java版)图2(最小生成树)

最小生成树举例
A
50 60 52 65 50
C
45 42 30 50
A
C
45
B
40
D
G
B
40 50
D
42 30
G
E
70
F
E
F
(a) 无向带权连通图G
(b) 无向带权图G 的最小生成树T
从最小生成树的定义可知,构造n个顶点的无向带权连 通图的最小生成树,必须满足如下三个条件: ① 必须包含n个顶点。 ② 有且仅有n-1条边。 ③ 没有回路。

将ej边加入到tree中;
}
实践项目

设计一个程序实现Prim和Kruskal算法.
表5-1 lowcost[ ]数组数据变化情况 表5-2 closest[ ]数组数据变化情况
扫描次数
closest[0]
closest[1]
closest[2]
closest[3]
closest[4]
closest[5]
求最小生成树算法

普里姆算法(Prim) (从点着手)

适合于求边稠密的最小生成树 适合于求边稀疏的最小生成树

克鲁斯卡尔算法(Kruskal)(从边着手)

普里姆算法(Prim)思想
1.
2.
3.
4.
令集合U={u0}(即从顶点u0开始构造最小生 成树),集合T={}。 从所有顶点u∈U和顶点v∈V-U的边权中选择最 小权值的边(u,v),将顶点v加入到集合U中,边 (u,v)加入到集合T中。 如此重复下去,直到U=V时则最小生成树构造完 毕。 此时集合U就是最小生成树的顶点集合,集合T 就是最小生成树的边集。

数据结构最小生成树克鲁斯卡尔算法代码

数据结构最小生成树克鲁斯卡尔算法代码

数据结构最小生成树克鲁斯卡尔算法代码以下是克鲁斯卡尔算法的实现代码:```python# 定义边的类class Edge:def __init__(self, src, dest, weight):self.src = srcself.dest = destself.weight = weight# 定义图的类class Graph:def __init__(self, vertices):self.V = verticesself.edges = []def add_edge(self, src, dest, weight):edge = Edge(src, dest, weight)self.edges.append(edge)# 查找顶点的根节点def find(self, parent, i):if parent[i] == i:return ireturn self.find(parent, parent[i])# 合并两个集合def union(self, parent, rank, x, y):xroot = self.find(parent, x)yroot = self.find(parent, y)if rank[xroot] < rank[yroot]:parent[xroot] = yrootelif rank[xroot] > rank[yroot]:parent[yroot] = xrootelse:parent[yroot] = xrootrank[xroot] += 1# 使用克鲁斯卡尔算法计算最小生成树def kruskal(self):result = []i = 0e = 0 # 已选择的边的数量# 按权重对边进行排序self.edges = sorted(self.edges, key=lambda x:x.weight)parent = []rank = []# 初始化每个顶点的父节点和秩for node in range(self.V):parent.append(node)rank.append(0)while e < self.V - 1:# 选择权重最小的边edge = self.edges[i]i += 1x = self.find(parent, edge.src)y = self.find(parent, edge.dest) # 判断是否形成环路if x != y:e += 1result.append(edge)self.union(parent, rank, x, y) return result# 测试代码g = Graph(4)g.add_edge(0, 1, 10)g.add_edge(0, 2, 6)g.add_edge(0, 3, 5)g.add_edge(1, 3, 15)g.add_edge(2, 3, 4)result = g.kruskal()for edge in result:print(f"{edge.src} -- {edge.dest} == {edge.weight}")```这段代码实现了克鲁斯卡尔算法,其中`Graph`类表示图,`Edge`类表示边。

数据结构的树应用中的问题

数据结构的树应用中的问题

数据结构的树应用中的问题树是一种重要的数据结构,在计算机科学中有着广泛的应用。

树的应用涉及到许多问题,本文将介绍其中一些常见的问题及其解决方法。

一、二叉搜索树的查找二叉搜索树是一种特殊的树结构,它的每个节点都包含一个值,并且左子树的值小于该节点的值,右子树的值大于该节点的值。

在二叉搜索树中,我们可以通过比较节点的值来快速地进行查找操作。

具体的查找方法可以使用递归或迭代的方式实现,通过不断比较节点的值,直到找到目标节点或者遍历到叶子节点为止。

二、二叉树的遍历二叉树的遍历是指按照一定的顺序访问二叉树中的所有节点。

常用的遍历方式有前序遍历、中序遍历和后序遍历。

前序遍历是指先访问根节点,然后按照先左后右的顺序遍历左右子树;中序遍历是指先遍历左子树,然后访问根节点,最后遍历右子树;后序遍历是指先遍历左右子树,最后访问根节点。

这三种遍历方式在不同的问题中有着不同的应用,具体的选择取决于问题的要求。

三、树的高度和深度树的高度和深度是指从根节点到叶子节点的最长路径上的节点数。

计算树的高度可以使用递归的方法,分别计算左子树和右子树的高度,然后取较大值再加上根节点即可。

树的深度可以通过求解根节点到目标节点的路径长度来实现,具体方法可以使用递归或迭代的方式。

四、树的平衡性检查树的平衡性是指树的左右子树的高度差不超过一个固定值。

平衡树的好处是可以提高树的查找效率。

常见的平衡树有AVL树和红黑树。

平衡树的插入和删除操作会涉及到旋转操作,通过旋转可以调整树的结构以保持平衡。

五、树的最小生成树最小生成树是指在一个加权连通图中选择一棵包含所有顶点的树,使得树的总权值最小。

常用的算法有Prim算法和Kruskal算法。

Prim算法是一种贪心算法,从一个起始点开始,每次选择与当前树相连的最小权值的边,逐步扩展生成树。

Kruskal算法则是一种基于并查集的算法,首先将图中的边按照权值从小到大排序,然后逐步选择权值最小且不会形成环的边加入生成树。

实验5最小生成树算法的设计与实现(报告)

实验5最小生成树算法的设计与实现(报告)

实验5 最小生成树算法的设计与实现一、实验目的1、根据算法设计需要, 掌握连通图的灵活表示方法;2、掌握最小生成树算法,如Prim、Kruskal算法;3、基本掌握贪心算法的一般设计方法;4、进一步掌握集合的表示与操作算法的应用。

二、实验内容1、认真阅读算法设计教材和数据结构教材内容, 熟习连通图的不同表示方法和最小生成树算法;2、设计Kruskal算法实验程序。

有n个城市可以用(n-1)条路将它们连通,求最小总路程的和。

设计测试问题,修改并调试程序, 输出最小生成树的各条边, 直至正确为止。

三、Kruskal算法的原理方法边权排序:1 3 14 6 23 6 41 4 52 3 53 4 52 5 61 2 63 5 65 6 61. 初始化时:属于最小生成树的顶点U={}不属于最小生成树的顶点V={1,2,3,4,5,6}2. 根据边权排序,选出还没有连接并且权最小的边(1 3 1),属于最小生成树的顶点U={1,3},不属于最小生成树的顶点V={2,4,5,6}3. 根据边权排序,选出还没有连接并且权最小的边(4 6 2),属于最小生成树的顶点U={{1,3},{4,6}}(还没有合在一起,有两颗子树),不属于最小生成树的顶点V={2,5}4. 根据边权排序,选出还没有连接并且权最小的边(3 6 4),属于最小生成树的顶点U={1,3,4,6}(合在一起),不属于最小生成树的顶点V={2,5}5. 根据边权排序,选出还没有连接并且权最小的边(3 6 4),属于最小生成树的顶点U={1,2,3,4,6},,不属于最小生成树的顶点V={5}6. 根据边权排序,选出还没有连接并且权最小的边(3 6 4),属于最小生成树的顶点U={1,2,3,4,5,6}此时,最小生成树已完成四、实验程序的功能模块功能模块:bool cmp(Edge a,Edge b); //定义比较方法x);//在并查集森林中找到x的祖先int g etfa(intint s ame(int x,int y); //判断祖先是否是同一个,即是否联通 void merge(int x,int y); //合并子树,即联通两子树sort(e+1,e+m+1,cmp); //对边按边权进行升序排序详细代码:#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#define M AXN_E 100000#define M AXN_V 100000using namespace std;struct Edge{int f m,to,dist;//边的起始顶点,边的到达顶点,边权}e[MAXN_E];int f a[MAXN_V],n,m; //顶点数组,顶点总数,边总数 //定义比较,只是边权比较bool cmp(Edge a,Edge b){return a.dist < b.dist;}//查找x的祖先是在并查集森林中找到x的祖先x){//getfaint g etfa(intreturn fa[x];if(fa[x]==x)else r eturn fa[x] = getfa(fa[x]);}//判断祖先是否是同一个,即是否联通int s ame(int x,int y){return getfa(x)==getfa(y);}//合并两棵树void merge(int x,int y){int f ax=getfa(x),fay=getfa(y);fa[fax]=fay;}int m ain(){int i;cout<<"请输入顶点数目和边数目:"<<endl;cin>>n>>m;//n为点数,m为边数//输出顶点信息cout<<"各个顶点值依次为:"<<endl;for(i=0;i<n;i++){fa[i]=i;if(i!=0)cout<<fa[i]<<" ";}cout<<endl;cout<<"请输入边的信息(例子:1 4 5 从顶点1到顶点4的边权为5)"<<endl;for(i=1;i<=m;i++)用边集数组存放边,方便排序和调用 cin>>e[i].fm>>e[i].to>>e[i].dist;//sort(e+1,e+m+1,cmp); //对边按边权进行升序排序表示目前的点共存在于多少个集合中,初始情况是每 int r st=n,ans=0;//rst个点都在不同的集合中for(i=1;i<=m && rst>1;i++){int x=e[i].fm,y=e[i].to;函数是查询两个点是否在同一集合中 if(same(x,y))continue;//sameelse{函数用来将两个点合并到同一集合中 merge(x,y);//mergerst--;//每次将两个不同集合中的点合并,都将使rst值减1这条边是最小生成树中的边,将答案加上边权 ans+=e[i].dist;//}}cout<<ans;return 0;}五、测试数据和相应的最小生成树Input:6 101 2 61 3 11 4 52 3 52 5 63 4 53 5 63 6 44 6 25 6 6Putout:18生成树为:七、思考题1、微软面试题一个大院子里住了50户人家,每家都养了一条狗,有一天他们接到通知说院子里有狗生病了,并要求所有主人在发现自己家狗生病的当天就要把狗枪杀掉。

考研数据结构图的必背算法及知识点

考研数据结构图的必背算法及知识点

考研数据结构图的必背算法及知识点Prepared on 22 November 20201.最小生成树:无向连通图的所有生成树中有一棵边的权值总和最小的生成树问题背景:假设要在n个城市之间建立通信联络网,则连通n个城市只需要n—1条线路。

这时,自然会考虑这样一个问题,如何在最节省经费的前提下建立这个通信网。

在每两个城市之间都可以设置一条线路,相应地都要付出一定的经济代价。

n个城市之间,最多可能设置n(n-1)/2条线路,那么,如何在这些可能的线路中选择n-1条,以使总的耗费最少呢分析问题(建立模型):可以用连通网来表示n个城市以及n个城市间可能设置的通信线路,其中网的顶点表示城市,边表示两城市之间的线路,赋于边的权值表示相应的代价。

对于n个顶点的连通网可以建立许多不同的生成树,每一棵生成树都可以是一个通信网。

即无向连通图的生成树不是唯一的。

连通图的一次遍历所经过的边的集合及图中所有顶点的集合就构成了该图的一棵生成树,对连通图的不同遍历,就可能得到不同的生成树。

图G5无向连通图的生成树为(a)、(b)和(c)图所示:G5G5的三棵生成树:可以证明,对于有n个顶点的无向连通图,无论其生成树的形态如何,所有生成树中都有且仅有n-1条边。

最小生成树的定义:如果无向连通图是一个网,那么,它的所有生成树中必有一棵边的权值总和最小的生成树,我们称这棵生成树为最小生成树,简称为最小生成树。

最小生成树的性质:假设N=(V,{E})是个连通网,U是顶点集合V的一个非空子集,若(u,v)是个一条具有最小权值(代价)的边,其中,则必存在一棵包含边(u,v)的最小生成树。

解决方案:两种常用的构造最小生成树的算法:普里姆(Prim)和克鲁斯卡尔(Kruskal)。

他们都利用了最小生成树的性质1.普里姆(Prim)算法:有线到点,适合边稠密。

时间复杂度O(N^2)假设G=(V,E)为连通图,其中V为网图中所有顶点的集合,E为网图中所有带权边的集合。

数据结构 耿国华 西北大学 7-5最小生成树-动画演示

数据结构 耿国华 西北大学 7-5最小生成树-动画演示

5
6
小 D 6 6 18 14
生 成 树
E F
1291
11
18 14
33
33
Step1. 初始化。选取种子 顶点,种子顶点构成最优 路段网,其余顶点构成外 围网。初始化Closedge。
A
16 B
21
1
2
3
4
5
6
A A A A A A U 19
F
C
A B C D E FV
0 16 ∞ ∞ 19 21 w
Closedge数组
F
21 Step3. 更新Closedge, 11 确保每组边是最优路段网
到外围网路径中最小的。
14
33
A
16
B
5
6
11
AB U 19
F
C
FV
6
2111 w
E
D
最小生成树——Prim算法
AB C DE
A 16 19
B 16 5 6
最M
C
5
6
Hale Waihona Puke 小 D 6 6 18E
D
Closedge数组
最小生成树——Prim算法
AB C DE
A 16 19
B 16 5 6
最M
C
5
6
小 D 6 6 18
生 成 树
E F
1291
11
18 14
33
1
2
3
4
5
A A AB AB A
ABCDE
0 0 ∞5 ∞6 19
Closedge数组
F
21 Step3. 更新Closedge, 11 确保每组边是最优路段网

最小生成树的算法

最小生成树的算法

最小生成树的算法王洁引言:求连通图的最小生成树是数据结构中讨论的一个重要问题.在现实生活中,经常遇到如何得到连通图的最小生成树,求最小生成树不仅是图论的基本问题之一 ,在实际工作中也有很重要的意义,,人们总想寻找最经济的方法将一个终端集合通过某种方式将其连接起来 ,比如将多个城市连为公路网络 ,要设计最短的公路路线;为了解决若干居民点供水问题 ,要设计最短的自来水管路线等.而避开这些问题的实际意义 ,抓住它们的数学本质 ,就表现为最小生成树的构造。

下面将介绍几种最小生成树的算法。

一,用“破圈法”求全部最小生成树的算法1 理论根据1.1 约化原则给定一无向连通图 G =(V ,E )( V 表示顶点,E 表示边),其中 V={ 1v , 2v ,3v …… n v },E= { 1e , 2e , 3e …… n e }对于 G 中的每条边 e ∈ E 都赋予权ω(i e )>0,求生成树 T = (V ,H ),H ⊆ E ,使生成树所有边权最小,此生成树称为最小生成树.(1) 基本回路将属于生成树 T 中的边称为树枝,树枝数为n -1,不属于生成树的边称为连枝.将任一连枝加到生成树上后都会形成一条回路.把这种回路称为基本回路,记为()cf e 。

基本回路是由 T 中的树枝和一条连枝构成的回路.(2) 基本割集设无向图 G 的割集 S (割集是把连通图分成两个分离部分的最少支路集合) ,若 S 中仅包含有T 中的一条树枝,则称此割集为基本割集,记为()S e 。

基本割集是集合中的元素只有一条是树枝,其他的为连枝.(3) 等长变换设T=(V,H),为一棵生成树,e ∈ H, 'e ∈ E, 'e ∉ H,当且仅当'e ∈()cf e ,也就是说e ∈()S e ,则'T =T ⊕{e, 'e }也是一棵生成树。

当()e ω='()e ω时,这棵生成树叫做等长变换。

最小生成树

最小生成树
然后从那些其一个端点已在U中,另一个端点 仍在U外的所有边中,找一条最短(即权值最 小)的边,假定该边为(Vi,Vj),其中Vi∈U, Vj∈V-U,并把该边(Vi,Vj)和顶点Vj分别并入T的 边集TE和顶点集U; 图
如此进行下去,每次往生成树里并入一 个顶点和一条边,直到n-1次后,把所有 n 个顶点都并入生成树T的顶点集U中, 此时U=V,TE中包含有(n-1)条边;

图6.10 图G 及其生成树
无向连通图 G 图
➢ 生成树
图6.10 图G 及其生成树
生成树
➢ 最小生成树

1.1 普里姆(prim)算法
假设G=(V,E)是一个具有n 个顶点的连通网络, T=(U,TE)是G的最小生成树,其中U是T的顶点 集,TE是T的边集,U和TE的初值均为空。
算法开始时,首先从V中任取一个顶点(假定 为V1),将此顶点并入U中,此时最小生成树 顶点集U={V1};
这样,T就是最后得到的最小生成树。
普里姆算法中每次选取的边两端,总是 一个已连通顶点(在U集合内)和一个未 连通顶点(在U集合外),故这个边选取 后一定能将未连通顶点连通而又保证不 会形成环路。

图6.11 普里姆算法例子

为了便于在顶点集合U和V-U之间选择权 最小的边,建立两个数组closest和 lowcost,closest[i]表示U中的一个顶点,该 顶点与V-U中的一个顶点构成的边具有最 小的权;lowcost表示该边对应的权值。

{

min=lowcost[j];

k=j;

} printf(“(%d,%d)”,k,closest[j]);
/* 打印生成树的一条边*/

《数据结构》课程设计 普里姆算法 最小生成树

《数据结构》课程设计 普里姆算法 最小生成树
printf("(%d %d %d)\n",lge[i].start_vex,lge
[i].stop_vex,lge[i].weight); /*输出N-1条最小边的信息*/
for(i=0;i<12;i++)
{
line(vex[lge[i].start_vex][0],vex[lge[i].start_vex][1],vex[lge
lge[min]=lge[i];
lge[i]=edge;
vx=lge[i].stop_vex;
for(j=i+1; j<pgraph->n-1; j++)
{
vy=lge[j].stop_vex;
weight=pgraph->arcs[vx][vy];
if(weight<lge[j].weight)
{
{550,250},{520,330},{430,400},{350,450},{270,400},{200,330}};
/*初始化个顶点的坐标*/
int info[12][12];
char *text;
void initalGraph(int vec[][2]) /*画出顶点函数*/
{
int gd=DETECT,gm;
[i].stop_vex][0],vex[lge[i].stop_vex][1]);
}
/*根据生成的最小边数组连线*/
printf("---It is done!---");
getch();
exit(1);
}
此程序再TURBOC2.0环境中编译通过运行.TURBOC2.0下载的地址

最小生成树

最小生成树
16
17
D
应用举例——最小生成树
Prim算法 34 A 46 19 F B 12 26 25 E 38 cost'={(A, B)34, (C, D)17, (F, E)26}
17
U={A, F, C, D}
V-U={B, E}
cost={(A, B)34, (F, E)26}
25
C
17
D
应用举例——最小生成树
{B, C, D, E, F} (A F)19
{B, C, D, E} {B, D, E} {B, E} {B} {}
(F C)25
(C D)17
(F E)26
adjvex lowcost
adjvex lowcost
4 12
{A, F, C, D, E}
{A,F,C,D,E,B}
(E B)12
21
应用举例——最小生成树
Prim算法——伪代码
1. 将顶点0加入集合U中; 2. 初始化辅助数组shortEdge,分别为lowcost和adjvex赋值; 3. 重复执行下列操作n-1次 3.1 在shortEdge中选取最短边,取其对应的下标k; 3.2 输出边(k, shortEdge[k].adjvex)和对应的权值; 3.3 将顶点k加入集合U中; 3.4 调整数组shortEdge;
U
V-U
输出
adjvex lowcost adjvex lowcost adjvex lowcost adjvex lowcost
0 34 0 34 0 34 0 34
0 46 5 25
0 ∞ 5 25 2 17
0 ∞ 5 26 5 26 5 26
0 19

数据结构之最小生成树Prim算法

数据结构之最小生成树Prim算法

数据结构之最⼩⽣成树Prim算法普⾥姆算法介绍 普⾥姆(Prim)算法,是⽤来求加权连通图的最⼩⽣成树算法 基本思想:对于图G⽽⾔,V是所有顶点的集合;现在,设置两个新的集合U和T,其中U⽤于存放G的最⼩⽣成树中的顶点,T存放G的最⼩⽣成树中的边。

从所有uЄU,vЄ(V-U) (V-U表⽰出去U的所有顶点)的边中选取权值最⼩的边(u, v),将顶点v加⼊集合U中,将边(u, v)加⼊集合T中,如此不断重复,直到U=V为⽌,最⼩⽣成树构造完毕,这时集合T中包含了最⼩⽣成树中的所有边。

代码实现1. 思想逻辑 (1)以⽆向图的某个顶点(A)出发,计算所有点到该点的权重值,若⽆连接取最⼤权重值#define INF (~(0x1<<31)) (2)找到与该顶点最⼩权重值的顶点(B),再以B为顶点计算所有点到改点的权重值,依次更新之前的权重值,注意权重值为0或⼩于当前权重值的不更新,因为1是⼀当找到最⼩权重值的顶点时,将权重值设为了0,2是会出现⽆连接的情况。

(3)将上述过程⼀次循环,并得到最⼩⽣成树。

2. Prim算法// Prim最⼩⽣成树void Prim(int nStart){int i = 0;int nIndex=0; // prim最⼩树的索引,即prims数组的索引char cPrims[MAX]; // prim最⼩树的结果数组int weights[MAX]; // 顶点间边的权值cPrims[nIndex++] = m_mVexs[nStart].data;// 初始化"顶点的权值数组",// 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。

for (i = 0; i < m_nVexNum; i++){weights[i] = GetWeight(nStart, i);}for (i = 0; i < m_nVexNum; i ++){if (nStart == i){continue;}int min = INF;int nMinWeightIndex = 0;for (int k = 0; k < m_nVexNum; k ++){if (weights[k]!= 0 && weights[k] < min){min = weights[k];nMinWeightIndex = k;}}// 找到下⼀个最⼩权重值索引cPrims[nIndex++] = m_mVexs[nMinWeightIndex].data;// 以找到的顶点更新其他点到该点的权重值weights[nMinWeightIndex]=0;int nNewWeight = 0;for (int ii = 0; ii < m_nVexNum; ii++){nNewWeight = GetWeight(nMinWeightIndex, ii);// 该位置需要特别注意if (0 != weights[ii] && weights[ii] > nNewWeight){weights[ii] = nNewWeight;}}for (i = 1; i < nIndex; i ++){int min = INF;int nVexsIndex = GetVIndex(cPrims[i]);for (int kk = 0; kk < i; kk ++){int nNextVexsIndex = GetVIndex(cPrims[kk]);int nWeight = GetWeight(nVexsIndex, nNextVexsIndex);if (nWeight < min){min = nWeight;}}nSum += min;}// 打印最⼩⽣成树cout << "PRIM(" << m_mVexs[nStart].data <<")=" << nSum << ": ";for (i = 0; i < nIndex; i++)cout << cPrims[i] << "";cout << endl;}3. 全部实现#include "stdio.h"#include <iostream>using namespace std;#define MAX 100#define INF (~(0x1<<31)) // 最⼤值(即0X7FFFFFFF)class EData{public:EData(char start, char end, int weight) : nStart(start), nEnd(end), nWeight(weight){} char nStart;char nEnd;int nWeight;};// 边struct ENode{int nVindex; // 该边所指的顶点的位置int nWeight; // 边的权重ENode *pNext; // 指向下⼀个边的指针};struct VNode{char data; // 顶点信息ENode *pFirstEdge; // 指向第⼀条依附该顶点的边};// ⽆向邻接表class listUDG{public:listUDG(){};listUDG(char *vexs, int vlen, EData **pEData, int elen){m_nVexNum = vlen;m_nEdgNum = elen;// 初始化"邻接表"的顶点for (int i = 0; i < vlen; i ++){m_mVexs[i].data = vexs[i];m_mVexs[i].pFirstEdge = NULL;}char c1,c2;int p1,p2;ENode *node1, *node2;// 初始化"邻接表"的边for (int j = 0; j < elen; j ++){// 读取边的起始顶点和结束顶点p1 = GetVIndex(c1);p2 = GetVIndex(c2);node1 = new ENode();node1->nVindex = p2;node1->nWeight = pEData[j]->nWeight;if (m_mVexs[p1].pFirstEdge == NULL){m_mVexs[p1].pFirstEdge = node1;}else{LinkLast(m_mVexs[p1].pFirstEdge, node1);}node2 = new ENode();node2->nVindex = p1;node2->nWeight = pEData[j]->nWeight;if (m_mVexs[p2].pFirstEdge == NULL){m_mVexs[p2].pFirstEdge = node2;}else{LinkLast(m_mVexs[p2].pFirstEdge, node2);}}}~listUDG(){ENode *pENode = NULL;ENode *pTemp = NULL;for (int i = 0; i < m_nVexNum; i ++){pENode = m_mVexs[i].pFirstEdge;if (pENode != NULL){pTemp = pENode;pENode = pENode->pNext;delete pTemp;}delete pENode;}}void PrintUDG(){ENode *pTempNode = NULL;cout << "邻接⽆向表:" << endl;for (int i = 0; i < m_nVexNum; i ++){cout << "顶点:" << GetVIndex(m_mVexs[i].data)<< "-" << m_mVexs[i].data<< "->"; pTempNode = m_mVexs[i].pFirstEdge;while (pTempNode){cout <<pTempNode->nVindex << "->";pTempNode = pTempNode->pNext;}cout << endl;}}// Prim最⼩⽣成树void Prim(int nStart){int i = 0;int nIndex=0; // prim最⼩树的索引,即prims数组的索引char cPrims[MAX]; // prim最⼩树的结果数组int weights[MAX]; // 顶点间边的权值cPrims[nIndex++] = m_mVexs[nStart].data;// 初始化"顶点的权值数组",// 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。

最小生成树定理

最小生成树定理

摘要:最小生成树(Minimum Spanning Tree,MST)是图论中的一个基本概念,它在网络设计、数据结构、算法设计等领域有着广泛的应用。

本文将详细介绍最小生成树定理的定义、性质、算法以及在实际应用中的重要性。

一、引言在图论中,一个图由顶点和边组成。

如果这个图是一个连通图,那么它的任意两个顶点之间都存在一条路径。

最小生成树定理研究的是如何从一个连通无向图中选择一些边,使得这些边构成一个连通子图,并且所有边的权值之和最小。

二、定义1. 图:由顶点集合V和边集合E组成,记为G=(V,E),其中V表示顶点集,E表示边集。

2. 连通图:对于图G中的任意两个顶点u、v,若存在一条路径连接u和v,则称图G是连通的。

3. 无向图:对于图G中的任意两个顶点u、v,若边(u,v)和边(v,u)同时存在,则称边(u,v)为无向边。

4. 权值:对于图G中的任意一条边(u,v),可以赋予一个非负实数w(u,v)作为权值。

5. 最小生成树:对于图G的一个连通子图T,如果满足以下两个条件,则称T为G 的最小生成树:(1)T是连通的;(2)T中的边权值之和最小。

三、性质1. 存在性:对于任意一个连通无向图,都存在一个最小生成树。

2. 唯一性:对于任意一个连通无向图,其最小生成树是唯一的。

3. 极小性:对于任意一个连通无向图,它的最小生成树中的边权值之和最小。

4. 极大性:对于任意一个连通无向图,它的最小生成树中的边权值之和最大。

四、算法1. 克鲁斯卡尔算法(Kruskal's Algorithm)(1)将图G中的所有边按照权值从小到大排序;(2)初始化一个空的最小生成树T;(3)遍历排序后的边,对于每条边(u,v):①检查边(u,v)是否与T中的任意一条边形成环;②若不形成环,则将边(u,v)加入T;(4)当T中的边数等于顶点数减1时,算法结束。

2. 普里姆算法(Prim's Algorithm)(1)从图G中选择一个顶点作为起始顶点v0;(2)初始化一个空的最小生成树T,并将v0加入T;(3)对于图G中的其他顶点v,初始化一个距离数组dist,其中dist[v]表示顶点v到T的距离,初始时dist[v]=∞(v≠v0);(4)遍历T中的顶点,对于每个顶点v,更新其相邻顶点的距离;(5)从距离数组中选择距离最小的顶点u,将其加入T;(6)重复步骤(4)和(5),直到T中的边数等于顶点数减1。

经典算法题每日演练——第十四题Prim算法

经典算法题每日演练——第十四题Prim算法

经典算法题每⽇演练——第⼗四题Prim算法图论在数据结构中是⾮常有趣⽽复杂的,作为web码农的我,在实际开发中⼀直没有找到它的使⽤场景,不像树那样的频繁使⽤,不过还是准备仔细的把图论全部过⼀遍。

⼀:最⼩⽣成树图中有⼀个好玩的东西叫做⽣成树,就是⽤边来把所有的顶点联通起来,前提条件是最后形成的联通图中不能存在回路,所以就形成这样⼀个推理:假设图中的顶点有n个,则⽣成树的边有n-1条,多⼀条会存在回路,少⼀路则不能把所有顶点联通起来,如果⾮要在图中加上权重,则⽣成树中权重最⼩的叫做最⼩⽣成树。

对于上⾯这个带权⽆向图来说,它的⽣成树有多个,同样最⼩⽣成树也有多个,因为我们⽐的是权重的⼤⼩。

⼆:Prim算法求最⼩⽣成树的算法有很多,常⽤的是Prim算法和Kruskal算法,为了保证单⼀职责,我把Kruskal算法放到下⼀篇,那么Prim算法的思想是什么呢?很简单,贪⼼思想。

如上图:现有集合M={A,B,C,D,E,F},再设集合N={}。

第⼀步:挑选任意节点(⽐如A),将其加⼊到N集合,同时剔除M集合的A。

第⼆步:寻找A节点权值最⼩的邻节点(⽐如F),然后将F加⼊到N集合,此时N={A,F},同时剔除M集合中的F。

第三步:寻找{A,F}中的权值最⼩的邻节点(⽐如E),然后将E加⼊到N集合,此时N={A,F,E},同时剔除M集合的E。

最后M集合为{}时,⽣成树就构建完毕了,是不是⾮常的简单,这种贪⼼做法我想⼤家都能想得到,如果算法配合⼀个好的数据结构,就会如虎添翼。

图的存储有很多⽅式,邻接矩阵,邻接表,⼗字链表等等,当然都有⾃⼰的适合场景,下⾯⽤邻接矩阵来玩玩,邻接矩阵需要采⽤两个数组,①. 保存顶点信息的⼀维数组,②. 保存边信息的⼆维数组。

1 public class Graph2 {3 /// <summary>4 /// 顶点个数5 /// </summary>6 public char[] vertexs;78 /// <summary>9 /// 边的条数10 /// </summary>11 public int[,] edges;1213 /// <summary>14 /// 顶点个数15 /// </summary>16 public int vertexsNum;1718 /// <summary>19 /// 边的个数20 /// </summary>21 public int edgesNum;22 }2:矩阵构建矩阵构建很简单,这⾥把上图中的顶点和权的信息保存在矩阵中。

数据结构:第7章 图3-最小生成树

数据结构:第7章 图3-最小生成树

• 按照生成树的定义,n 个顶点的连通网络的生成树有 n
个顶点、n-1 条边。
即有权图
目标:
在网络的多个生成树中,寻找一个各边权值之和最小的
生成树。
构造最小生成树的准则 ❖ 必须只使用该网络中的边来构造最小生成树;
❖ 必须使用且仅使用n-1条边来联结网络中的n个顶点;
❖ 不能使用产生回路的边。
典型用途:
(b) u={1} w={2,3,4,5,6}
0 6 1 5
6
0
5
3
1 5 0 7 5 4
5
7
0
2
3 5 0 6
4 2 6 0
i
1234
closest[i] 1 1 1 1
lowcost[i] 0 6 1 5
56 11 ∞∞
closest用于存放顶点序号 lowest存放权值
15 4 6
1 25
3
54
5
6
(c ) u={1,3} w={2,4,5,6}
1
1
4
25
6
32
54
5
6
(d) u={1,3,6} w={2,4,5}
i
1234 5 6
closest[i] 1 3 1 1 3 3
lowcost[i] 0 5 0 5 5 4
i
1234 5 6
closest[i] 1 3 1 6 3 3

v3 v1

树 v4 v2
v1
0^ 1^ 0^ 1^
2.生成森林
若一个图是非连通图或非强连通图,但有若 干个连通分量或若干个强连通分量,则通过 深度优先搜索遍历或广度优先搜索遍历,不 可以得到生成树,但可以得到生成森林,且 若非连通图有 n 个顶点,m 个连通分量或强 连通分量,则可以遍历得到m棵生成树,合 起来为生成森林,森林中包含n-m条树边。

《数据结构MST》课件

《数据结构MST》课件

VS
最小生成树唯一性的证明
通过反证法,假设存在两棵不同的最小生 成树,它们之间至少存在一个边不同,那 么可以通过调整这条边来得到一棵权值和 更小的生成树,与最小生成树的定义矛盾 。
最小生成树性质
最小生成树的边数
01
在一个连通加权无向图中,最小生成树的边数等于顶点数减一

最小生成树的权值和
02
在一个连通加权无向图中,最小生成树的权值和等于所有边的
最小生成图问题
1 2 3
定义
给定一个带权重的连通图,寻找一种方式将该图 分解为若干个子图,使得所有子图的权值和最小 。
算法
可以采用Kruskal算法或Prim算法进行扩展,通 过将多个顶点划分为一个集合,然后寻找连接这 些集合的边,形成子图。
应用
在图像处理、化学分子结构分析等领域有广泛应 用,用于简化模型和提高计算效率。
电力系统网络优化
在电力系统中,最小生成树算法可以用于构 建低损耗的输电网络,提高电力传输效率。
最小生成树算法的实现步骤
选择起始节点
选择一个节点作为最小生成树 的根节点。
构建最小生成树
从根节点开始,按照权值从小 到大选择边,直到所有节点都 被连接起来。
构建无向图
将问题转化为无向图,并确定 所有节点和边。
要点二
克鲁斯卡尔算法(Kruskal's Algorithm)
按照边的权值从小到大排序,依次选择边,如果选择的边 不会与已选择的边构成环,则加入到生成树中,直到所有 顶点都连接在一起。
02
MST基本算法
Prim算法
总结词
Prim算法是一种求解最小生成树问题的贪心算法。
详细描述
Prim算法从图中的任意一个顶点开始,每次选择距离已选顶点集合最近的顶点 加入集合,直到所有顶点都被加入。该算法利用了贪心策略,每次选择局部最 优解,最终得到全局最优解。

最小生成树实验报告

最小生成树实验报告

最小生成树实验报告最小生成树(Minimum Spanning Tree,MST)是图论中的一个重要概念,用于在一个连通带权无向图中找到一个子图,使得这个子图是一个树(即无环连通图),并且所有边的权值之和最小。

最小生成树在诸多领域有着广泛的应用,如网络设计、电力传输等。

在本次实验中,我们实现了最小生成树算法,并将其运用到多个实际问题上。

下面将依次介绍算法原理、实现过程、实验结果以及对实验的进一步改进。

1.算法原理Kruskal算法的基本思想是,首先将所有边按照权值从小到大排序,然后从最小的边开始,逐一加入生成树,直到生成树包含了所有的顶点。

在加入一条边时,需要判断这条边将两个顶点连通起来是否会形成环,如果不会则加入生成树。

Prim算法的基本思想是,从一个顶点开始,逐步加入生成树的顶点,每次加入一个顶点时,选择一个离生成树最近的点,并将这个点加入生成树。

通过不断的选择顶点和加入边,最终得到最小生成树。

2.实现过程首先,我们实现了图的数据结构和边的数据结构。

在图的数据结构中,我们定义了图的顶点数和边数,并用邻接矩阵来表示图的连接情况。

边的数据结构包含了两个顶点和边的权值。

其次,我们实现了两种算法。

对于Kruskal算法,我们首先将所有边按照权值从小到大进行排序。

然后,逐个加入边,判断是否形成环。

如果不会形成环,则将该边加入生成树。

最后,我们使用并查集数据结构来判断两个顶点是否连通。

对于Prim算法,我们首先选择一个起点作为生成树的初始顶点,并将其加入生成树。

然后,每次选择一个离生成树最近的顶点,并将其加入生成树,同时更新其他顶点到生成树的距离。

最后,所有顶点都被加入生成树后,得到最小生成树。

3.实验结果我们在实验中选择了不同大小的图进行测试。

经过对比,我们发现Kruskal算法和Prim算法得到的最小生成树结果是一致的,但是Kruskal 算法的时间复杂度要稍高于Prim算法。

具体的结果如下:对于一个边数为10的图,我们得到了如下最小生成树:1-2-4-5-3总权重为12对于一个边数为20的图,我们得到了如下最小生成树:2-1-4-5-3总权重为16对于一个边数为30的图2-1-4-5-6-7-3总权重为22从实验结果来看,无论是规模较小的图还是规模较大的图,我们都能够得到最小生成树,并且所得到的结果是正确的。

数据结构最小生成树头歌c语言

数据结构最小生成树头歌c语言

数据结构最小生成树头歌c语言一、前言数据结构中的最小生成树是一种非常重要的算法,它在网络设计、图像处理和社交网络分析等领域有着广泛的应用。

在C语言中,我们可以通过不同的数据结构来实现最小生成树算法,本文将重点介绍如何使用头歌C语言来实现最小生成树算法。

二、数据结构介绍1. 什么是最小生成树在一个连通的无向图中,如果我们要找到一个生成树,并且这个生成树的所有边的权值之和最小,那么这个生成树就是最小生成树。

最小生成树算法有Prim算法和Kruskal算法两种经典的实现方式。

2. 最小生成树的应用最小生成树广泛应用于各种场景中。

比如在计算机网络中,我们可以通过最小生成树算法来构建网络拓扑结构;在社交网络分析中,我们可以基于用户间的关系构建最小生成树,从而发现社区结构;在传感器网络中,最小生成树算法可以用于构建相互连接的传感器网络。

三、 Kruskal算法实现Kruskal算法是一种贪心算法,它的基本思想是:首先将图中的边按照权值的大小进行排序,然后依次加入权值最小的边,并且保证加入的边不会构成回路,直到生成树的边数等于节点数减一为止。

在C语言中,我们可以通过以下步骤来实现Kruskal算法:1. 定义一个结构体来表示边的信息,包括起点、终点和边的权值。

2. 根据边的权值对边进行排序。

3. 使用并查集来判断是否构成回路,如果不构成回路则加入最小生成树中。

4. 重复步骤3直到生成树的边数等于节点数减一。

实际的C语言代码如下所示:```c#include <stdio.h>#include <stdbool.h>#define MAX_EDGE 100#define MAX_VERT 100typedef struct{int from, to;int weight;} Edge;Edge edges[MAX_EDGE];int parent[MAX_VERT];void make_set(int v){parent[v] = v;}int find_set(int v){if (v == parent[v])return v;return parent[v] = find_set(parent[v]); }void union_sets(int a, int b){a = find_set(a);b = find_set(b);if (a != b)parent[b] = a;}int m本人n(){int n, m; // n为节点数,m为边数scanf("dd", n, m);for (int i = 0; i < m; i++)scanf("ddd", edges[i].from, edges[i].to, edges[i].weight);for (int i = 1; i <= n; i++)make_set(i);// 对边按照权值进行排序for (int i = 0; i < m; i++){for (int j = 0; j < m - 1 - i; j++){if (edges[j].weight > edges[j + 1].weight){Edge tmp = edges[j];edges[j] = edges[j + 1];edges[j + 1] = tmp;}}}int cost = 0;for (int i = 0; i < m; i++){if (find_set(edges[i].from) != find_set(edges[i].to)) {printf("d d d\n", edges[i].from, edges[i].to, edges[i].weight);cost += edges[i].weight;union_sets(edges[i].from, edges[i].to);}}printf("Minimum cost = d\n", cost);return 0;}```四、 Prim算法实现Prim算法是另一种最小生成树算法,在C语言中同样可以通过数据结构来实现。

数据结构和算法学习笔记八:带权连通图的最小生成树

数据结构和算法学习笔记八:带权连通图的最小生成树

数据结构和算法学习笔记⼋:带权连通图的最⼩⽣成树⼀.简介: 对于⼀个n个顶点的连通图,其最⼩⽣成树是指将所有顶点连接起来的权值之和的最⼩树,树中包含n个顶点和n-1条边.最⼩⽣成树常见的⽣成算法有普⾥姆算法和克鲁斯卡尔算法,它们分别基于顶点的⾓度和边的⾓度⽣成最⼩⽣成树. 声明:对于本⽂中实现图结构的各种类,详见:⼆.两种算法简介 1.普⾥姆算法:普⾥姆算法基于顶点实现,基本思路是将所有已经纳⼊到最⼩⽣成树中的顶点存储起来,然后遍历当前的最⼩⽣成树的端点,找出权值最⼩且不会闭环的边并延伸最⼩⽣成树,然后将新的顶点纳⼊到最⼩⽣成树中(和其他已经纳⼊到树中的顶点⼀起存储起来) 2.克鲁斯卡尔算法:克鲁斯卡尔算法基于边实现,⾸先将所有边按照权值由⼩到⼤排序,然后再从⼩到达依次遍历所有边,⼀⼀判断当前边加⼊最⼩⽣成树中后是否会形成环路,在不形成环路的情况下将此边加⼊最⼩⽣成树,并将顶点存储起来.顶点的存储结构类似于倒置的树,根节点在最下⽅.在最⼩⽣成树的⽣成过程中可能会同时存在多颗顶点树,但是最终所有顶点树会汇聚成⼀颗.三.代码实现(c#)/************************************* 创建⼈:movin* 创建时间:2021/7/4 19:55:02* 版权所有:个⼈***********************************/using System;using System.Collections.Generic;using System.Text;namespace GraphCore{///<summary>///最⼩⽣成树算法///</summary>public class MinimumCostSpanningTreeUtil{///<summary>///计算最⼩⽣成树-普⾥姆算法///要求参数必须是⼀个连通图,此处没有校验参数graph是否是连通图的过程,可⾃⾏添加///</summary>///<param name="graph"></param>///<param name="findAEdgeCallBack">找到⼀条边后的回调函数,参数为边的两个关联点下标和权值</param>public static void MiniSpanTree_Prim(AdjacencyMatrixGraph graph,Action<int,int,int> findAEdgeCallBack = null){//数组lowcast,数组的长度和顶点的个数⼀致,数组中每个下标的值和顶点⼀⼀对应//lowcast的作⽤有两个,以lowcast[1] = 5为例,意思是当前已经找过的顶点中到1顶点的最短路径权值为5//所以作⽤⼀是某下标对应值不为0时代表当前已经⽣成的部分最⼩⽣成树到某下标对应顶点的权值最⼩的边的权值//作⽤⼆是某下标对应值为0时代表此下标对应顶点已经在最⼩⽣成树中,不再参与继续⽣成最⼩⽣成树int[] lowcast = new int[graph.Count];//数组adjvex,这个数组作⽤是对应记录lowcast中最⼩权值边的另⼀个依附顶点下标(⼀个依附顶点下标就是lowcast下标)int[] adjvex = new int[graph.Count];lowcast[0] = 0;//从0号顶点开始⽣成最⼩⽣成树,⾸先将0号顶点对应位置置为0//adjvex[0] = 0;//这句代码加不加都ok,0号位已经加⼊最⼩⽣成树,这个值也就⽤不上了//初始化lowcast数组的其他下标值for(int i = 1;i < lowcast.Length; i++){//当前最⼩⽣成树中只有0号顶点,所以以0号顶点到i号顶点的边的权值就是当前的最⼩边权值lowcast[i] = graph.adjacencyMatrix[0, i];//这些边的另⼀个依附顶点当然是0号顶点adjvex[i] = 0;}//开始计算最⼩⽣成树,结果存储到result中int min = int.MaxValue;//⽤来存储找到的最⼩权值边的权值的临时变量int tempIndex = 0;//⽤来存储即将加⼊最⼩⽣成树的边的顶点(也就是即将加⼊最⼩⽣成树的顶点)的临时变量,另⼀个顶点存储在adjvex数组中//循环length-1次,每次将⼀个顶点和⼀条边加⼊最⼩⽣成树中for(int i = 1;i < graph.Count; i++){//循环在当前的lowcast中找到⾮0的最⼩值(到没有找过的顶点中的最⼩边)min = int.MaxValue;tempIndex = 0;for(int j = 1;j < lowcast.Length; j++){if(lowcast[j] != 0 && lowcast[j] < min){min = lowcast[j];tempIndex = j;}}//找到边后调⽤回调函数if(findAEdgeCallBack != null){findAEdgeCallBack(tempIndex, adjvex[tempIndex], lowcast[tempIndex]);}//更新lowcast数组lowcast[tempIndex] = 0;//每次延申了最⼩⽣成树后需要将lowcast中的值更新,⽅便下次继续延申最⼩⽣成树//刚才将下标为tempIndex的顶点和⼀条边加⼊了最⼩⽣成树,接下来只需要更新这个顶点相关的边即可for(int j = 1;j < lowcast.Length;j++){//判断顶点tempIndex和顶点j之间的边//j顶点不在最⼩⽣成树中且这条边的权值⽐lowcast中记录的最⼩权值要⼩时//更新到顶点j的最⼩权值边的权值,并且记录到顶点j的最⼩权值边的另⼀个顶点为tempIndexif(lowcast[j] != 0 && lowcast[j] > graph.adjacencyMatrix[tempIndex, j]){lowcast[j] = graph.adjacencyMatrix[tempIndex, j];adjvex[j] = tempIndex;}}}}///<summary>///计算最⼩⽣成树-克鲁斯卡尔算法///要求参数必须是连通图///</summary>///<param name="graph"></param>///<param name="findAEdgeCallBack">找到⼀条边后的回调函数,参数为边的两个关联点下标和权值</param>public static void MinSpanTree_Kruskal(EdgesetArrayGraph graph, Action<int, int, int> findAEdgeCallBack = null){//将边集数组排序SortEdgeNode(graph.edgeNodes);//声明⼀个数组,数组下标对应顶点下标//数组中值为-1时代表对应顶点还没有加⼊最⼩⽣成树//当某个顶点被加⼊最⼩⽣成树后,将数组中对应的下标的值修改,修改后的值指向下⼀个加⼊最⼩⽣成树的顶点下标//如vertices[5] = 7代表5号顶点和7号顶点都在最⼩⽣成树中,其中5号顶点的下⼀个顶点是7号顶点//在构建最⼩⽣成树的过程中会通过这个数组检验当前边添加进数组是否会构成环//分析后⾯的代码可以知道,最终数组中length-1个值会被修改,刚好对应添加到最⼩⽣成树中的length-1条边int[] vertices = new int[graph.edgeNodes.Length];//数组初始值都为-1for (int i = 0; i < vertices.Length; i++){vertices[i] = -1;}//下⾯构建最⼩⽣成树//循环遍历所有边,⼀⼀校验是否可以加⼊最⼩⽣成树for (int i = 0; i < graph.edgeNodes.Length; i++){EdgesetArrayEdgeNode node = graph.edgeNodes[i];int startIndex = GetNextVertex(vertices, node.headIndex);int endIndex = GetNextVertex(vertices, node.tailIndex);//检验是否成环,不成环则这条边可以加⼊最⼩⽣成树if (startIndex != endIndex){vertices[startIndex] = endIndex;if(findAEdgeCallBack != null){findAEdgeCallBack(node.headIndex, node.tailIndex, node.weight);}}}}///<summary>///在vertices中,顶点之间的先后次序最终的存储⽅式类似于⼀颗倒过来的树,根顶点在最下⽅,存储时会⼀直向下找,直到找到根顶点,存储时会将下⼀个存储到最⼩⽣成树中的顶点挂到根顶点下⽅成为新///查找时看此顶点是否有后继顶点,如果有那么继续查找后继顶点的后继顶点...以此类推,直到某个顶点对应下标值为-1,即没有后继顶点,返回这个顶点下标///如果两个顶点之间会构成环路,那么它们所在的顶点的后继中⼀定会有相同的顶点,最终查找下去得到的值为顶点相同///</summary>///<param name="vertices"></param>///<param name="index"></param>///<returns></returns>private static int GetNextVertex(int[] vertices,int index){while(vertices[index] != -1){index = vertices[index];}return index;}///<summary>///将给定边集数组按照从⼩到达排序///采⽤选择排序///</summary>///<param name="graph"></param>private static void SortEdgeNode(EdgesetArrayEdgeNode[] edgeNodes){for (int i = 0; i < edgeNodes.Length; i++){int minIndex = i;for (int j = i + 1; j < edgeNodes.Length; j++){if(edgeNodes[minIndex].weight > edgeNodes[j].weight){minIndex = j;}}if(minIndex != i){EdgesetArrayEdgeNode temp = edgeNodes[i];edgeNodes[i] = edgeNodes[minIndex];edgeNodes[minIndex] = temp;}}}}}。

最小生成树prim算法实验报告

最小生成树prim算法实验报告

最小生成树prim算法实验报告最小生成树Prim算法实验报告引言:最小生成树(Minimum Spanning Tree,简称MST)是图论中的一个重要概念,意为在一个连通图中找到一棵生成树,使得树上所有边的权值之和最小。

Prim算法是一种常用的解决MST问题的贪心算法。

本实验旨在通过实际操作和观察,深入理解Prim算法的原理与过程。

实验目的:1. 理解Prim算法的基本原理;2. 掌握Prim算法的具体实现过程;3. 利用Prim算法求解最小生成树问题;4. 分析Prim算法的时间复杂度。

实验过程:1. 实验环境搭建:在实验开始前,我们需要搭建合适的实验环境。

首先,我们选择一种编程语言,如Python或C++,来实现Prim算法。

其次,我们需要准备一个图的数据集,可以是随机生成的或者是从现实问题中提取的。

最后,我们需要一个用于可视化的工具,以便观察Prim算法的执行过程和结果。

2. Prim算法实现:Prim算法的核心思想是从一个顶点开始,逐步扩展生成树,直到包含所有顶点为止。

具体实现过程如下:a. 初始化一个空的生成树,选择一个起始顶点;b. 在剩余的顶点中,选择与生成树距离最近的顶点,并将其加入生成树;c. 更新生成树与剩余顶点的距离,如果存在更短的路径,则更新;d. 重复步骤b和c,直到生成树包含所有顶点。

3. Prim算法求解最小生成树问题:利用Prim算法求解最小生成树问题的步骤如下:a. 根据实验环境搭建中准备的图数据集,构建图的邻接矩阵或邻接表表示;b. 选择一个起始顶点,将其加入生成树;c. 重复以下步骤,直到生成树包含所有顶点:i. 从生成树中选择一个顶点v,找到与v相连的顶点中距离最小的顶点u; ii. 将顶点u加入生成树,并将(u, v)边加入生成树的边集;iii. 更新生成树与剩余顶点的距离,如果存在更短的路径,则更新。

实验结果与分析:我们通过实验环境搭建和Prim算法实现,成功求解了多个最小生成树问题。

数据结构(三十三)最小生成树(Prim、Kruskal)

数据结构(三十三)最小生成树(Prim、Kruskal)

数据结构(三⼗三)最⼩⽣成树(Prim、Kruskal) ⼀、最⼩⽣成树的定义 ⼀个连通图的⽣成树是⼀个极⼩的连通⼦图,它含有图中全部的顶点,但只有⾜以构成⼀棵树的n-1条边。

在⼀个⽹的所有⽣成树中,权值总和最⼩的⽣成树称为最⼩代价⽣成树(Minimum Cost Spanning Tree),简称为最⼩⽣成树。

构造最⼩⽣成树的准则有以下3条:只能使⽤该图中的边构造最⼩⽣成树当且仅当使⽤n-1条边来连接图中的n个顶点不能使⽤产⽣回路的边 对⽐两个算法,Kruskal算法主要是针对边来展开,边数少时效率会⾮常⾼,所以对于稀疏图有很⼤的优势;⽽Prim算法对于稠密图,即边数⾮常多的情况会更好⼀些。

⼆、普⾥姆(Prim)算法 1.Prim算法描述 假设N={V,{E}}是连通⽹,TE是N上最⼩⽣成树中边的集合。

算法从U={u0,u0属于V},TE={}开始。

重复执⾏下⾯的操作:在所有u属于U,v 属于V-U的边(u,v)中找⼀条代价最⼩的边(u0,v0)并加⼊集合TE,同时v0加⼊U,直到U=V为⽌。

此时TE中必有n-1条边,则T=(V,{TE})为N的最⼩⽣成树。

2.Prim算法的C语⾔代码实现/* Prim算法⽣成最⼩⽣成树 */void MiniSpanTree_Prim(MGraph G){int min, i, j, k;int adjvex[MAXVEX]; /* 保存相关顶点下标 */int lowcost[MAXVEX]; /* 保存相关顶点间边的权值 */lowcost[0] = 0;/* 初始化第⼀个权值为0,即v0加⼊⽣成树 *//* lowcost的值为0,在这⾥就是此下标的顶点已经加⼊⽣成树 */adjvex[0] = 0; /* 初始化第⼀个顶点下标为0 */for(i = 1; i < G.numVertexes; i++) /* 循环除下标为0外的全部顶点 */{lowcost[i] = G.arc[0][i]; /* 将v0顶点与之有边的权值存⼊数组 */adjvex[i] = 0; /* 初始化都为v0的下标 */}for(i = 1; i < G.numVertexes; i++){min = INFINITY; /* 初始化最⼩权值为∞, *//* 通常设置为不可能的⼤数字如32767、65535等 */j = 1;k = 0;while(j < G.numVertexes) /* 循环全部顶点 */{if(lowcost[j]!=0 && lowcost[j] < min)/* 如果权值不为0且权值⼩于min */{min = lowcost[j]; /* 则让当前权值成为最⼩值 */k = j; /* 将当前最⼩值的下标存⼊k */}j++;}printf("(%d, %d)\n", adjvex[k], k);/* 打印当前顶点边中权值最⼩的边 */lowcost[k] = 0;/* 将当前顶点的权值设置为0,表⽰此顶点已经完成任务 */for(j = 1; j < G.numVertexes; j++) /* 循环所有顶点 */{if(lowcost[j]!=0 && G.arc[k][j] < lowcost[j]){/* 如果下标为k顶点各边权值⼩于此前这些顶点未被加⼊⽣成树权值 */lowcost[j] = G.arc[k][j];/* 将较⼩的权值存⼊lowcost相应位置 */adjvex[j] = k; /* 将下标为k的顶点存⼊adjvex */}}}}Prim算法 3.Prim算法的Java语⾔代码实现package bigjun.iplab.adjacencyMatrix;/*** 最⼩⽣成树之Prim算法*/public class MiniSpanTree_Prim {int lowCost; // 顶点对应的权值public CloseEdge(Object adjVex, int lowCost) {this.adjVex = adjVex;this.lowCost = lowCost;}}private static int getMinMum(CloseEdge[] closeEdges) {int min = Integer.MAX_VALUE; // 初始化最⼩权值为正⽆穷int v = -1; // 顶点数组下标for (int i = 0; i < closeEdges.length; i++) { // 遍历权值数组,找到最⼩的权值以及对应的顶点数组的下标if (closeEdges[i].lowCost != 0 && closeEdges[i].lowCost < min) {min = closeEdges[i].lowCost;v = i;}}return v;}// Prim算法构造图G的以u为起始点的最⼩⽣成树public static void Prim(AdjacencyMatrixGraphINF G, Object u) throws Exception{// 初始化⼀个⼆维最⼩⽣成树数组minSpanTree,由于最⼩⽣成树的边是n-1,所以数组第⼀个参数是G.getVexNum() - 1,第⼆个参数表⽰边的起点和终点符号,所以是2 Object[][] minSpanTree = new Object[G.getVexNum() - 1][2];int count = 0; // 最⼩⽣成树得到的边的序号// 初始化保存相关顶点和相关顶点间边的权值的数组对象CloseEdge[] closeEdges = new CloseEdge[G.getVexNum()];int k = G.locateVex(u);for (int j = 0; j < G.getVexNum(); j++) {if (j!=k) {closeEdges[j] = new CloseEdge(u, G.getArcs()[k][j]);// 将顶点u到其他各个顶点权值写⼊数组中}}closeEdges[k] = new CloseEdge(u, 0); // 加⼊u到⾃⾝的权值0for (int i = 1; i < G.getVexNum(); i++) { // 注意,这⾥从1开始,k = getMinMum(closeEdges); // 获取u到数组下标为k的顶点的权值最短minSpanTree[count][0] = closeEdges[k].adjVex; // 最⼩⽣成树第⼀个值为uminSpanTree[count][1] = G.getVexs()[k]; // 最⼩⽣成树第⼆个值为k对应的顶点count++;closeEdges[k].lowCost = 0; // 下标为k的顶点不参与最⼩权值的查找了for (int j = 0; j < G.getVexNum(); j++) {if (G.getArcs()[k][j] < closeEdges[j].lowCost) {closeEdges[j] = new CloseEdge(G.getVex(k), G.getArcs()[k][j]);}}}System.out.print("通过Prim算法得到的最⼩⽣成树序列为: {");for (Object[] Tree : minSpanTree) {System.out.print("(" + Tree[0].toString() + "-" + Tree[1].toString() + ")");}System.out.println("}");}} 4.举例说明Prim算法实现过程 以下图为例: 测试类:// ⼿动创建⼀个⽤于测试最⼩⽣成树算法的⽆向⽹public static AdjacencyMatrixGraphINF createUDNByYourHand_ForMiniSpanTree() {Object vexs_UDN[] = {"V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8"};int arcsNum_UDN = 15;int[][] arcs_UDN = new int[vexs_UDN.length][vexs_UDN.length];for (int i = 0; i < vexs_UDN.length; i++) // 构造⽆向图邻接矩阵for (int j = 0; j < vexs_UDN.length; j++)if (i==j) {arcs_UDN[i][j]=0;} else {arcs_UDN[i][j] = arcs_UDN[i][j] = INFINITY;}arcs_UDN[0][5] = 11;arcs_UDN[1][2] = 18;arcs_UDN[1][6] = 16;arcs_UDN[1][8] = 12;arcs_UDN[2][3] = 22;arcs_UDN[2][8] = 8;arcs_UDN[3][4] = 20;arcs_UDN[3][6] = 24;arcs_UDN[3][7] = 16;arcs_UDN[3][8] = 21;arcs_UDN[4][5] = 26;arcs_UDN[4][7] = 7;arcs_UDN[5][6] = 17;arcs_UDN[6][7] = 19;for (int i = 0; i < vexs_UDN.length; i++) // 构造⽆向图邻接矩阵for (int j = i; j < vexs_UDN.length; j++)arcs_UDN[j][i] = arcs_UDN[i][j];return new AdjMatGraph(GraphKind.UDN, vexs_UDN.length, arcsNum_UDN, vexs_UDN, arcs_UDN);}public static void main(String[] args) throws Exception {AdjMatGraph UDN_Graph = (AdjMatGraph) createUDNByYourHand_ForMiniSpanTree();MiniSpanTree_Prim.Prim(UDN_Graph, "V0");} 输出为:通过Prim算法得到的最⼩⽣成树序列为: {(V0-V1)(V0-V5)(V1-V8)(V8-V2)(V1-V6)(V6-V7)(V7-V4)(V7-V3)} 分析算法执⾏过程:从V0开始:-count为0,k为0,closeEdges数组的-lowCost为{0 10 INF INF INF 11 INF INF INF},adjVex数组为{V0,V0,V0,V0,V0,V0,V0,V0,V0}-⽐较lowCost,于是k为1,adjVex[1]为V0,minSpanTree[0]为(V0,V1),lowCost为{0 0 INF INF INF 11 INF INF INF}-k为1,与V1的权值⾏⽐较,得到新的-lowCost为:{0 0 18 INF INF 11 16 INF 12},adjVex数组为{V0,V0,V1,V0,V0,V0,V1,V0,V1}-⽐较lowCost,于是k为5,adjVex[5]为V0,minSpanTree[1]为(V0,V5),lowCost为{0 0 18 INF INF 0 16 INF 12}-k为5,与V5的权值⾏⽐较,得到新的-lowCost为{0 0 18 INF 26 0 16 INF 12},adjVex数组为{V0,V0,V1,V0,V5,V0,V1,V0,V1}-⽐较lowCost,于是k为8,adjVex[8]为V1,minSpanTree[2]为(V1,V8),lowCost为{0 0 18 INF INF 0 16 INF 0}... 三、克鲁斯卡尔(Kruskal)算法 1.Kruskal算法描述 Kruskal算法是根据边的权值递增的⽅式,依次找出权值最⼩的边建⽴的最⼩⽣成树,并且规定每次新增的边,不能造成⽣成树有回路,直到找到n-1条边为⽌。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

数据结构
多元化考核作业
题目:最小生成树
姓名:卢伟
专业班级:物联网工程B1501班
学号:
目录
1 课程设计介绍 (3)
1.1 课程设计内容 (3)
1.2 课程设计要求 (3)
2 课程设计原理 (4)
2.1 课设题目粗略分析 (4)
2.2 原理图介绍 (6)
2.2.1 功能模块图 (6)
2.2.2 流程图分析 (7)
3 数据结构分析 (13)
3.1 存储结构 (13)
3.2 算法描述 (13)
4 调试与分析 (14)
4.1 调试过程 (14)
1.2 程序执行过程 (15)
参考文献 (18)
1 课程设计介绍
1.1 课程设计内容
编写算法能够建立带权图,并能够用Kruskal算法求该图的最小生成树。

最小生成树能够选择图上的任意一点做根结点。

最小生成树输出采用顶点集合和边的集合的形式。

1.2 课程设计要求
1.顶点信息用字符串,数据可自行设定。

2.参考相应的资料,独立完成课程设计任务。

3.交规范课程设计报告和软件代码。

2 课程设计原理
2.1 课设题目粗略分析
根据课设题目要求,拟将整体程序分为三大模块。

以下是三个模块的大体分析:
1.要确定图的存储形式,通过对题目要求的具体分析。

发现该题的主要操作是路径的输出,因此采用边集数组(每个元素是一个结构体,包括起点、终点和权值)和邻接矩阵比较方便以后的编程。

2.Kruskal算法。

该算法设置了集合A,该集合一直是某最小生成树的子集。

在每步决定是否把边(u,v)添加到集合A中,其添加条件是A∪{(u,v)}仍然是最小生成树的子集。

我们称这样的边为A的安全边,因为可以安全地把它添加到A中而不会破坏上述条件。

3.Dijkstra算法。

算法的基本思路是:假设每个点都有一对标号(d j,p j),其中d是从起源点到点j的最短路径的长度(从顶点到其本身的最短路径是零路(没有弧的路),其长度等于零);p j则是从s到j 的最短路径中j点的前一点。

求解从起源点s到点j的最短路径算法的基本过程如下:
1)初始化。

起源点设置为:①d s=0,p s为空;②所有其它点:d i=∞,p i=?;③标记起源点s,记k=s,其他所有点设为未标记的。

2)k到其直接连接的未标记的点j的距离,并设置:
d j=min[d j, d k+l kj]
式中,l kj是从点k到j的直接连接距离。

3)选取下一个点。

从所有未标记的结点中,选取d j中最小的一个i:
d i=min[d j, 所有未标记的点j]
点i就被选为最短路径中的一点,并设为已标记的。

4)找到点i的前一点。

从已标记的点中找到直接连接到i的点j*,作为前一点,设置:i=j*
5)标记点i。

如果所有点已标记,则算法完全推出,否则,记k=i,转到2)再继续。

而程序中求两点间最短路径算法。

其主要步骤是:
①调用dijkstra算法。

②将path中的第“终点”元素向上回溯至起点,并显示出来。

2.2 原理图介绍
2.2.1 功能模块图
图2.1功能模块图
2.2.2 流程图分析
1.主函数
图2.2 主函数流程图
2.insertsort函数
3.图2.3 insertsort函数流程图
3.Kruskal函数
图2.4 Kruskal函数流程图
4. dijkstra 函数
图2.5 dijkstra 函数流程图
5. printpath1函数
图2.6 printpath1函数流程图
6. printpath2函数
图2.7 printpath2函数流程图
3 数据结构分析
3.1 存储结构
定义一个结构体数组,其空间足够大,可将输入的字符串存于数组中。

struct edges
{int bv;
int tv;
int w;
};
3.2 算法描述
1. Kruskal函数:
因为Kruskal需要一个有序的边集数组,所以要先对边集数组排序。

其次,在执行中需要判断是否构成回路,因此还需另有一个判断函数seeks,在Kruskal中调用seeks。

2. dijkstra函数:
因为从一源到其余各点的最短路径共有n-1条,因此可以设一变量vnum作为计数器控制循环。

该函数的关键在于dist数组的重新置数。

该置数条件是:该顶点是被访问过,并且新起点到该点的权值加上新起点到源点的权值小于该点原权值。

因此第一次将其设为:if (s[w]==0&&cost[u][w]+dist[u]<dist[w])。

但是在实际运行中,发现有些路径的权值为负。

经过分析发现,因为在程序中∞由32767代替。

若cost[u][w]==32767,那么cost[u][w]+dist[u]肯定溢出主负值,因此造成权值出现负值。

但是如果cost[u][w]==32767,那么dist[w]肯定不需要重新置数。

所以将条件改为:if(s[w]==0&&cost[u][w]+dist[u]<dist[w]&&cost[u][w]!=32767)。

修改之后问题得到解决。

3. printpath1函数:
该函数主要用来输出源点到其余各点的最短路径。

因为在主函数调用该函数前,已经调用了dijkstra函数,所以所需的dist、path、s数组已经由dijkstra函数生成,因此在该函数中,只需用一变量控制循环,一一将path数组中的每一元素回溯至起点即可。

其关键在于不同情况下输出形式的不同。

4.printpath2函数:
该函数主要用来输出两点间的最短路径。

其主要部分与printpath1函数相同,只是无需由循环将所有顶点一一输出,只需将path数组中下标为v1的元素回溯至起点并显示出来。

4 调试与分析
4.1 调试过程
在调试程序时主要遇到一下几类问题:
1.有时函数中一些数组中的数据无法存储。

2.对其进行检验发现没有申请空间大小。

3.在源程序的开头用#define定义数值大小,在使用数组时亦可知道它的空间大小。

4.此函数中有时出现负值。

5.对其进行检验发现在程序中∞由32767代替。

若cost[u][w]==32767,那么cost[u][w]+dist[u]肯定溢出主负值,因此造成权值出现负值。

6.但是当cost[u][w]==32767,那么dist[w]肯定不需要重新置数。

所以将if(s[w]==0&&cost[u][w]+dist[u]<dist[w])改为:if(s[w]==0&&cost[u][w]+dist[u]<dist[w]&&cost[u][w]!=32767)。

问题得到解决。

1.2程序执行过程
系统使用说明:
1. 输入的数据可以是整数,字符串(如1,2,3);
2. 本系统可以建立带权图,并能够用Kruskal算法求改图的最小生成树。

而且能够选择图上的任意一点做根结点。

还能够求两点之间的最短距离。

3. 该系统会有菜单提示,进行选项:
1.kruskal
2.shortpath
3.shortpath between two point
4.exit
4.程序实际运行截图
图4.1 输入形式
图4.2 kruskal算法输出
图4.3 最短距离输出
参考文献
[1]《数据结构》(C语言版).严蔚敏,吴伟民.清华大学出版社.2007
[2]《算法设计与分析》.张德富.国防工业出版社.2009
[3]《计算机算法与程序设计》.朱青.清华大学出版社.2009
[4]《C程序设计语言》.徐宝文,李志.机械工业出版社.2004。

相关文档
最新文档