数据结构15--最小生成树
数据结构(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 就是最小生成树的边集。
生成树类型

生成树类型
生成树是计算机科学中的一种基本数据结构,用于解决与图相关的许多问题。
生成树可以分为以下几类:
1. 最小生成树:在一个加权无向连通图中找到一棵包含所有顶点的树,并且总权值最小。
2. 最大生成树:在一个加权无向连通图中找到一棵包含所有顶点的树,并且总权值最大。
3. 最近公共祖先树:在一棵树中找到两个节点的最近公共祖先,并建立一棵包含这两个节点以及它们的所有祖先节点的子树。
4. Huffman编码树:用于数据压缩,建立一棵二叉树,由该树的叶子节点表示待压缩的数据,通过该树的路径表示压缩后的数据。
5. B-树:用于数据库和文件系统中,能够存储大量数据且支持高效的查找、插入、删除等操作的平衡树。
生成树是计算机科学中非常重要的一种数据结构,具有广泛的应用。
数据结构最小生成树克鲁斯卡尔算法代码

数据结构最小生成树克鲁斯卡尔算法代码以下是克鲁斯卡尔算法的实现代码:```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`类表示边。
最小生成树的算法

最小生成树的算法王洁引言:求连通图的最小生成树是数据结构中讨论的一个重要问题.在现实生活中,经常遇到如何得到连通图的最小生成树,求最小生成树不仅是图论的基本问题之一 ,在实际工作中也有很重要的意义,,人们总想寻找最经济的方法将一个终端集合通过某种方式将其连接起来 ,比如将多个城市连为公路网络 ,要设计最短的公路路线;为了解决若干居民点供水问题 ,要设计最短的自来水管路线等.而避开这些问题的实际意义 ,抓住它们的数学本质 ,就表现为最小生成树的构造。
下面将介绍几种最小生成树的算法。
一,用“破圈法”求全部最小生成树的算法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 ω时,这棵生成树叫做等长变换。
克鲁斯卡尔算法求最小生成树的最短路径

克鲁斯卡尔算法求最小生成树的最短路径克鲁斯卡尔算法的核心思想是从图的边集中选取边来构建最小生成树,首先将图中的所有边按照权重进行排序。
然后依次取最小权重的边,如果这条边的加入不会形成环路,则将它加入最小生成树的边集中。
重复这个过程,直到最小生成树中的边数等于顶点数减一,或者所有的边都已经考虑过。
假设有一个包含n个顶点的带权无向图G=(V,E),其中,V表示顶点的集合,E表示边的集合。
假设我们要求解G的最小生成树。
1.初始化边集E'为空集,集合S={v},v是图中任意一个顶点。
2.对所有的边进行排序,按照边的权重从小到大排列。
3.从排序后的边集中依次选取边e,如果边e的两个顶点都不在集合S中,则将边e加入集合S,并加入边集E'中。
4.重复步骤3,直到E'中的边数等于n-1在克鲁斯卡尔算法中,需要使用并查集来判断选定的边e是否会形成环路。
并查集是一种数据结构,用于维护元素的等价关系。
它有两个主要操作,即查找和合并。
使用并查集的步骤如下:1.初始化并查集,使得每个元素都是一个单独的集合。
2.对每一条边e=(u,v),如果u和v在同一个集合中,则说明加入这条边会形成环路;否则,将这两个集合合并。
3.重复步骤2,直到所有的边都考虑过。
接下来,我们通过一个具体的例子来说明克鲁斯卡尔算法的具体过程。
假设有以下的带权无向图G=(V,E),其中,V={A,B,C,D,E,F,G},E为边的集合,每条边的权重如下:AE:5AB:7AG:8BF:7BC:9CD:5CG:9DE:15DF:6EG:11EF:8FG:9按照权重对边进行排序:CD:5AE:5DF:6AB:7BF:7EF:8AG:8CG:9BC:9FG:9DE:15EG:11从最小的边开始选取,首先选取CD:5,加入到最小生成树的边集中。
最小生成树的边集:{"CD:5"}接下来选取AE:5,加入到最小生成树的边集中。
数据结构(图)习题与答案

一、单选题1、设有5个结点的无向图,该图至少应有_________条边才能确保是一个连通图。
A.7B.8C.6D.5正确答案:A2、设图G=(V,VR),其中: V={A,B,C,D,G},VR={(A,C),(A,D),( B,C),(B,D) ,(G,C),(B,G)},则对应的图形为_________。
A.B.C.D.正确答案:C3、设某有向图中有n个顶点,则该有向图对应的邻接表中有_________个表头结点。
A.n-1B.n+2C.nD.n+1正确答案:C4、在一个无向图中所有顶点的度数之和等于所有边数的_________倍。
A.1B.2C.3D.1/2正确答案:B5、一个无向连通图的生成树是该连通图的_____。
A.极小连通子图B.强连通子图C.连通子图D.极大连通子图正确答案:A6、设某无向图中有n个顶点,则该无向图邻接矩阵的大小是_________。
A.n(n+1)/2B.(n-1)2C. n2D. (n+1)2正确答案:C7、设有n个顶点e条边的无向图,采用邻接矩阵作为物理结构,则删除与某顶点Vi 关联的所有边算法的时间复杂度为_________。
A.O(n2)B.O(n+e)C.O(n*e)正确答案:D8、设有n个顶点e条弧的有向图,采用邻接表作为物理结构,则求某顶点Vi度的算法的时间复杂度为_________。
A.O(n)B.O(n*e)C.O(n+e)D.O(n2)正确答案:C9、设无向图G=(V,E)和G'=(V',E'),如果G'是G的生成树,则下列说法中错误的是_____。
A.G'是G的连通分量B.G'是G的一个无环子图C.G'是G的极小连通子图且V=V'D.G'是G的子图正确答案:A10、设G是一个非连通的无向图,共有10条边,则该图至少有_____个顶点。
A.7B.6C.5D.8正确答案:B11、 n个顶点的有向图为强连通图时,至少含有________。
《数据结构》课程设计 普里姆算法 最小生成树

[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下载的地址
最小生成树

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
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
构造: 构造: 计算最小生成树的第k 计算最小生成树的第k长边
• 可行性。如果d等于第k长边的长度,将去掉前k-1 长的边,最小生成树将被分成k个连通支。由引理, 原图也将被分成k个连通支,满足连通支个数小于 等于k的要求。 • 最优性。如果d小于第k长边的长度,至少会去掉 前k长的边,最小生成树至少被分成k+1个连通支, 原图也至少被分成k+1个连通支,不满足要求。
通过函数top(i)计算顶点i所在子树的根
func top(i:longint):longint; /*计算和返回顶 计算和返回顶 所在并查集的代表顶点*/ 点i所在并查集的代表顶点 所在并查集的代表顶点 { if i<>f[i] Then f[i]←top(f[i]); /*若i非所在并 若 非所在并 查集的代表顶点,则沿f[i]递归 递归*/ 查集的代表顶点,则沿 递归 top←f[i]; /*返回顶点 所在并查集的代表顶点 返回顶点i所在并查集的代表顶点 返回顶点 所在并查集的代表顶点*/ };/*top*/
逆向思维
找到一个最小的d,使得连通支的 个数小于等于卫星设备的数目k。
引理
如果去掉所有长度大于d的边后,最小
生成树被分成k个连通支,那么图也被分 成k个连通支。
证明提示: 1. 反证法 2. 构造回路
证明
用反证法。假设原图被分割成k’(k‘≠k)个连通支,显然k’<k。根 据鸽巢原理,在某一连通支TK’中,最小生成树被分成了至少两部 分,不妨设其为T1,T2。因为T1和T2同属于一个连通支TK’,所 以一定存在x∈T1,y∈T2,w(x,y)≤d。又因为在整个最小生 成树中,所以x到y的路径中一定存在一条权值大于d的边(u,v) (否则x和y就不会分属于T1和T2了),w(x,y)≤d<w(u,v), 所以把(x,y)加入,把(u,v)去掉,将得到一棵总权值比最小生 成树还小的生成树。这显然是不可能的。所以,原命题成立。 (证毕)
Kruskal程序 程序
Const maxn=200; maxe=maxn*maxn; /*顶点数和边数的上限 顶点数和边数的上限*/ 顶点数和边数的上限 Type edgetype=Record /*边的类型 边的类型*/ 边的类型 x,y,c:longint; /*边权为 的边(x,y)*/ 边权为c的边 边权为 的边( , ) end; Var e:Array [1..maxe] of edgetype; /*边集,其中第 条边为 边集, 边集 其中第i条边为 (e[i].x,e[i].y),该边的权为 ,该边的权为e[i].c*/ n,m,ans:longint; /*顶点数为 ,边数为 顶点数为n,边数为m*/ 顶点数为 f:Array [1..maxn] of longint; /*并查集。其中 为顶点 所在 并查集。 为顶点i所在 并查集 其中f[i]为顶点 并查集的代表顶点,即子树根*/ 并查集的代表顶点,即子树根
计算最小生成树的思维方向: 计算最小生成树的思维方向:为了保证边权
总和最小,必须保证
①、添加(u,v)不能够形成回路、 ②、在保证1的前提下添加权尽可能小的, 这样的边称之为安全边
有两种算法可计算图的最小生成树 ①、Kruskal算法 ②、Prim算法
①、Kruskal算法 Kruskal算法
找出森林中连结任意两棵树的所有边中具有最小权值的边(u,v) 作为安全边,并把它添加到正在生长的森林中。初始时,森林 由单个结点组成的n棵树。
动态生成最小生成树
在一个初始化为空的无向图中,不断加入新边。如果 当前图连通,就求出当前图最小生成树的总权值;否 则,输出-1。 输入输出情况:第1行输入顶点数n和加入的边数k;以 下为2*k行,其中第2*i行输入‘x y c’,表示加入的第i 条边连接顶点x和y,权值为c;第2*i+1行输出回答: 最小生成树的总权值(如果当前图连通),或者-1 (如果当前图不连通)
图的最小生成树
对于一张图进行深度优先搜索或宽 度优先搜索, 度优先搜索,可生成一棵深度优先 搜索树或宽度优先搜索树。 搜索树或宽度优先搜索树。搜索的 出发点不同,生成树的形态亦不同。 出发点不同,生成树的形态亦不同。 在一张有权连通图中, 在一张有权连通图中,各边权和为 最小的一棵生成树即为最小生成树。 最小的一棵生成树即为最小生成树。
初始化
const maxn = 800; /*顶点数的上限 顶点数的上限*/ 顶点数的上限 var pnt:array[1..maxn]of integer;/*顶点 的父顶点为 顶点i的父顶点为 顶点 的父顶点为pnt[i]*/ e, c:array[1..maxn, 0..maxn] of integer; /*边表 ,其中顶点 相连的 边表e,其中顶点i相连的 边表 边数为e[i,0],第j条边的另一端点为 条边的另一端点为e[i,j],c为相邻矩阵 为相邻矩阵*/ 边数为 , 条边的另一端点为 , 为相邻矩阵 n,k,sum,t:integer; /*顶点数为 新添边数为 ,生成树的总权值为 顶点数为n新添边数为 顶点数为 新添边数为k, sum,边数为 ,边数为t*/ xi,yi,max:integer;/*当前路径的最大边权为 当前路径的最大边权为max,对应边为 对应边为(xi,yi)*/ 当前路径的最大边权为 对应边为 ok:boolean; /*成功标志 成功标志*/ 成功标志 proc Init; /*输入信息 输入信息*/ 输入信息 var i:integer; { readln(n, k); /*输入顶点数和加入的边数 输入顶点数和加入的边数*/ 输入顶点数和加入的边数 fillchar(e,sizeof(e),0);sum←0;t←0; /*边表、生成树的总权值和边数 边表、 边表 初始化*/ 初始化 for i←1 to n do pnt[i]←i; /*n个顶点组成 棵树 个顶点组成n棵树 个顶点组成 棵树*/ };/*Init*/
主算法
按照边权值(c域值)递增的顺序排序边集e; 按照边权值( 域值)递增的顺序排序边集 ; 域值 For i←1 to n Do f[i]←i; /*建立由 棵树组成的森林,每 建立由n棵树组成的森林 建立由 棵树组成的森林, 棵树包含图的一个顶点*/ 棵树包含图的一个顶点 ans←0; /* 最小生成树的权和初始化为 最小生成树的权和初始化为0*/ For i←1 to m Do Union(e[i].x,e[i].y,e[i].c); /*枚举每条边, 枚举每条边, 枚举每条边 将两个端点分属两棵树的边加入最小生成树*/ 将两个端点分属两棵树的边加入最小生成树 Writeln(ans); 显然, 算法的效率取决于边数m,因此适用与稀疏 显然 Kruskal算法的效率取决于边数 因此适用与稀疏 算法的效率取决于边数 图。
北极通讯网络
北极的某区域共有n座村庄(1 (1≤ 500), 北极的某区域共有n座村庄(1≤n≤500),每座村庄的坐标用一对 整数(x y)表示 (x, 表示, 10000。为了加强联系, 整数(x,y)表示,其中 0≤x,y≤10000。为了加强联系,决定在村庄 之间建立通讯网络。通讯工具可以是无线电收发机, 之间建立通讯网络。通讯工具可以是无线电收发机,也可以是卫星 设备。所有的村庄都可以拥有一部无线电收发机, 设备。所有的村庄都可以拥有一部无线电收发机, 且所有的无线电 收发机型号相同。但卫星设备数量有限, 收发机型号相同。但卫星设备数量有限,只能给一部分村庄配备卫 星设备。不同型号的无线电收发机有一个不同的参数d 星设备。不同型号的无线电收发机有一个不同的参数d,两座村庄之 间的距离如果不超过d就可以用该型号的无线电收发机直接通讯,d 间的距离如果不超过d就可以用该型号的无线电收发机直接通讯, 值越大的型号价格越贵。 值越大的型号价格越贵。拥有卫星设备的两座村庄无论相距多远都 可以直接通讯。 可以直接通讯。 现在有k 100)卫星设备,请你编一个程序, 现在有k台(0≤k≤100)卫星设备,请你编一个程序,计算出应 如何分配这k台卫星设备,才能使所拥有的无线电收发机的d值最小, 如何分配这k台卫星设备,才能使所拥有的无线电收发机的d值最小, 并保证每两座村庄之间都可直接或间接地通讯。请输出最小d值 并保证每两座村庄之间都可直接或间接地通讯。请输出最小d
图的最小生成树满足这个性质
反证法:反设图中存在一棵生成树T,T中的最大边权 小于最小生成树Tmin的最大边权。 令e是Tmin的最大边。e将Tmin分成两个连通分块X和Y。 由于Tmin是最小生成树,那么图中连接X和Y的任意边e’, 其边权都大于等于e的边权——结论1。 而T是图的一棵生成树,那么T中的最大边e’’连接X和 Y,按照假设,e’’的边权小于e的边权。这与结论1矛盾, 因此最小生成树最大边权最小的结论是成立的。 因此只要用Prim或Kruscarl算法求出图的最小生成树 即可。
数据限制:n≤500,k≤100。 K=2——B和C,min(d)=d(A,B)=10。
逆向思维
已知k,求d,比较困难。 已知d,求k,相对简单!
村庄设为顶点,所有可以 互相通讯的村庄连边,边 长为村庄间的距离,构造 一个图。图中卫星设备的 台数k就是图的连通支的个 数,每个连通支内顶点间 的边长不大于d。
通过过程Union(i,j,c)合并顶点i和顶点 j所在的两棵树
现有边权为c的边(i,j)。若该边的两个端点分 属于两棵树,顶点i和顶点j所在子树的根分别为x 和v,则(i,j) 加入最小生成树,合并两棵树(即顶 点i和顶点j所在的并查集)。
proc Union(i,j,c:longint); /*合并 和j所在集合 合并i和 所在集合 所在集合*/ 合并 Var x,y:longint; { x←top(i); y←top(j); /*分别取出顶点 和顶点 所在子 分别取出顶点i和顶点 分别取出顶点 和顶点j所在子 树的根*/ 树的根 if x<>y Then { inc(ans,c);f[y]←x;}; /*若i和j分属于两棵 若 和 分属于两棵 子树,则该边权计入最小生成树的权和,两棵子树合并 子树,则该边权计入最小生成树的权和, */ };/* Union */