数据结构最小生成树
- 格式:docx
- 大小:294.76 KB
- 文档页数:18
数据结构最小生成树克鲁斯卡尔算法代码以下是克鲁斯卡尔算法的实现代码:```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 最小生成树算法的设计与实现一、实验目的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为网图中所有带权边的集合。
最小生成树的算法王洁引言:求连通图的最小生成树是数据结构中讨论的一个重要问题.在现实生活中,经常遇到如何得到连通图的最小生成树,求最小生成树不仅是图论的基本问题之一 ,在实际工作中也有很重要的意义,,人们总想寻找最经济的方法将一个终端集合通过某种方式将其连接起来 ,比如将多个城市连为公路网络 ,要设计最短的公路路线;为了解决若干居民点供水问题 ,要设计最短的自来水管路线等.而避开这些问题的实际意义 ,抓住它们的数学本质 ,就表现为最小生成树的构造。
下面将介绍几种最小生成树的算法。
一,用“破圈法”求全部最小生成树的算法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 ω时,这棵生成树叫做等长变换。
数据结构之最⼩⽣成树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算法图论在数据结构中是⾮常有趣⽽复杂的,作为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:矩阵构建矩阵构建很简单,这⾥把上图中的顶点和权的信息保存在矩阵中。
最小生成树实验报告最小生成树(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语言来实现最小生成树算法。
二、数据结构介绍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语言中同样可以通过数据结构来实现。
数据结构
多元化考核作业
题目:最小生成树
姓名:卢伟
专业班级:物联网工程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。