PRIM算法求最小生成树

合集下载

最小生成树 实验报告

最小生成树 实验报告

最小生成树实验报告最小生成树实验报告一、引言最小生成树是图论中的一个重要概念,它在实际问题中有着广泛的应用。

本次实验旨在通过编程实现最小生成树算法,并通过实验数据对算法进行分析和评估。

二、算法介绍最小生成树算法的目标是在给定的带权无向图中找到一棵生成树,使得树上所有边的权重之和最小。

本次实验我们选择了两种经典的最小生成树算法:Prim 算法和Kruskal算法。

1. Prim算法Prim算法是一种贪心算法,它从一个顶点开始,逐步扩展生成树的规模,直到包含所有顶点为止。

算法的具体步骤如下:(1)选择一个起始顶点,将其加入生成树中。

(2)从与生成树相邻的顶点中选择一个权重最小的边,将其加入生成树中。

(3)重复上述步骤,直到生成树包含所有顶点。

2. Kruskal算法Kruskal算法是一种基于并查集的贪心算法,它首先将图中的边按权重从小到大进行排序,然后逐个加入生成树中,直到生成树包含所有顶点为止。

算法的具体步骤如下:(1)将图中的边按权重从小到大进行排序。

(2)逐个加入边,如果该边的两个顶点不在同一个连通分量中,则将其加入生成树中。

(3)重复上述步骤,直到生成树包含所有顶点。

三、实验过程本次实验我们使用C++语言实现了Prim算法和Kruskal算法,并通过随机生成的图数据进行了测试。

1. Prim算法的实现我们首先使用邻接矩阵表示图的结构,然后利用优先队列来选择权重最小的边。

具体实现过程如下:(1)创建一个优先队列,用于存储生成树的候选边。

(2)选择一个起始顶点,将其加入生成树中。

(3)将与生成树相邻的顶点及其边加入优先队列。

(4)从优先队列中选择权重最小的边,将其加入生成树中,并更新优先队列。

(5)重复上述步骤,直到生成树包含所有顶点。

2. Kruskal算法的实现我们使用并查集来维护顶点之间的连通关系,通过排序后的边序列来逐个加入生成树中。

具体实现过程如下:(1)将图中的边按权重从小到大进行排序。

prim算法和迪杰斯特拉算法

prim算法和迪杰斯特拉算法

prim算法和迪杰斯特拉算法在图算法中,Prim算法和Dijkstra算法都是常见且重要的算法。

它们分别用于解决图的最小生成树和最短路径问题。

无论是在实际应用还是在理论研究中,这两个算法都发挥着重要作用。

首先,我们来看一下Prim算法。

Prim算法是一种贪心算法,用于寻找一个连通加权无向图的最小生成树。

最小生成树是指一个图的一棵生成树,其所有边的权值之和最小。

Prim算法的基本思想是从一个起始顶点开始,不断选择与当前最小生成树连接的具有最小权值的边,将其加入生成树中,直到所有顶点都被加入生成树中。

这样就能得到原图的最小生成树。

以一个城市之间的道路网络为例,每条道路都有一定的长度,我们可以使用Prim算法来找到连接所有城市的最小道路网络。

在算法执行过程中,我们从任意一个城市开始,选择与当前已选城市集合连接的距离最短的道路,并将其加入道路网络中,直到所有城市都被连接起来。

这样,我们就得到了一个最小的道路网络,使得总长度最小。

接下来,我们来看一下Dijkstra算法。

Dijkstra算法是一种用于解决带权有向图的单源最短路径问题的算法。

最短路径问题是指找到从一个顶点到其他所有顶点的最短路径。

Dijkstra算法的基本思想是通过不断更新已找到的最短路径,逐步确定源顶点到其余顶点的最短路径。

具体实现时,可以使用一个优先队列来存储待处理的顶点,优先队列中的顶点按照从源顶点到该顶点的最短路径的长度进行排序。

每次从队列中取出最小距离的顶点进行处理,并更新与该顶点相邻顶点的最短路径。

在现实生活中,我们经常遇到需要找到最短路径的问题。

例如,我们在使用导航软件时,要找到最短路径来规划行车路线。

这时候,Dijkstra算法就可以派上用场。

我们可以将整个路网抽象成图,每个节点代表一个道路交叉口,每条边代表道路,边的权重可以表示行驶距离或时间。

通过运用Dijkstra算法,我们可以快速找到最短路线,节省时间和燃料。

总结来说,Prim算法和Dijkstra算法是解决图问题的两个重要算法。

prim算法求最小生成树c代码

prim算法求最小生成树c代码

一、概述在图论中,最小生成树是指在一个连通图中生成一棵包含图中所有顶点且边权值之和最小的树。

prim算法是一种常用的求取最小生成树的算法之一,其基本思想是从一个起始顶点开始,逐步选择与当前树相邻的并且权值最小的边,直到包含了图中所有的顶点为止。

本文将介绍prim算法的原理以及给出相应的C代码实现。

二、prim算法原理1. 初始化选择任意一个顶点作为起始顶点,并将其标记为已访问。

设置一个集合V来存放已经访问的顶点,初始化为空集合。

2. 重复以下步骤直到V包含了所有顶点:(1)遍历集合V中的所有顶点,找到与之相邻且未被访问过的顶点中边权值最小的顶点,将其加入集合V中。

(2)将找到的顶点与其相邻的边加入最小生成树中。

3. 输出最小生成树当集合V包含了所有顶点之后,即可输出最小生成树。

三、prim算法C代码实现以下是prim算法的C代码实现:#include <stdio.h>#include <limits.h>#define V 5 // 顶点个数int minKey(int key[], bool mstSet[]) {int min = INT_MAX, min_index;for (int v = 0; v < V; v++) {if (!mstSet[v] key[v] < min) {min = key[v], min_index = v;}}return min_index;}void printMST(int parent[], int graph[V][V]) {printf("Edge \tWeight\n");for (int i = 1; i < V; i++) {printf("d - d \td \n", parent[i], i, graph[i][parent[i]]);}void primMST(int graph[V][V]) {int parent[V]; // 存放最小生成树的结点int key[V]; // 存放与最小生成树相邻的边的权值bool mstSet[V]; // 存放已访问的顶点for (int i = 0; i < V; i++) {key[i] = INT_MAX, mstSet[i] = false;}key[0] = 0;parent[0] = -1;for (int count = 0; count < V - 1; count++) {int u = minKey(key, mstSet);mstSet[u] = true;for (int v = 0; v < V; v++) {if (graph[u][v] !mstSet[v] graph[u][v] < key[v]) { parent[v] = u, key[v] = graph[u][v];}}}printMST(parent, graph); }int m本人n() {int graph[V][V] = {{0, 2, 0, 6, 0},{2, 0, 3, 8, 5},{0, 3, 0, 0, 7},{6, 8, 0, 0, 9},{0, 5, 7, 9, 0}};primMST(graph);return 0;}```四、代码说明1. minKey函数该函数用于在尚未加入最小生成树的结点中找到与最小生成树相邻的边中权值最小的结点。

最小生成树---普里姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)

最小生成树---普里姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)

最⼩⽣成树---普⾥姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)最⼩⽣成树的性质:MST性质(假设N=(V,{E})是⼀个连通⽹,U是顶点集V的⼀个⾮空⼦集,如果(u,v)是⼀条具有最⼩权值的边,其中u属于U,v属于V-U,则必定存在⼀颗包含边(u,v)的最⼩⽣成树)普⾥姆算法(Prim算法)思路:以点为⽬标构建最⼩⽣成树1.将初始点顶点u加⼊U中,初始化集合V-U中各顶点到初始顶点u的权值;2.根据最⼩⽣成树的定义:从n个顶点中,找出 n - 1条连线,使得各边权值最⼩。

循环n-1次如下操作:(1)从数组lowcost[k]中找到vk到集合U的最⼩权值边,并从数组arjvex[k] = j中找到该边在集合U中的顶点下标(2)打印此边,并将vk加⼊U中。

(3)通过查找邻接矩阵Vk⾏的各个权值,即vk点到V-U中各顶点的权值,与lowcost的对应值进⾏⽐较,若更⼩则更新lowcost,并将k存⼊arjvex数组中以下图为例#include<bits/stdc++.h>using namespace std;#define MAXVEX 100#define INF 65535typedef char VertexType;typedef int EdgeType;typedef struct {VertexType vexs[MAXVEX];EdgeType arc[MAXVEX][MAXVEX];int numVertexes, numEdges;}MGraph;void CreateMGraph(MGraph *G) {int m, n, w; //vm-vn的权重wscanf("%d %d", &G->numVertexes, &G->numEdges);for(int i = 0; i < G->numVertexes; i++) {getchar();scanf("%c", &G->vexs[i]);}for(int i = 0; i < G->numVertexes; i++) {for(int j = 0; j < G->numVertexes; j++) {if(i == j) G->arc[i][j] = 0;else G->arc[i][j] = INF;}}for(int k = 0; k < G->numEdges; k++) {scanf("%d %d %d", &m, &n, &w);G->arc[m][n] = w;G->arc[n][m] = G->arc[m][n];}}void MiniSpanTree_Prim(MGraph G) {int min, j, k;int arjvex[MAXVEX]; //最⼩边在 U集合中的那个顶点的下标int lowcost[MAXVEX]; // 最⼩边上的权值//初始化,从点 V0开始找最⼩⽣成树Tarjvex[0] = 0; //arjvex[i] = j表⽰ V-U中集合中的 Vi点的最⼩边在U集合中的点为 Vjlowcost[0] = 0; //lowcost[i] = 0表⽰将点Vi纳⼊集合 U ,lowcost[i] = w表⽰ V-U中 Vi点到 U的最⼩权值for(int i = 1; i < G.numVertexes; i++) {lowcost[i] = G.arc[0][i];arjvex[i] = 0;}//根据最⼩⽣成树的定义:从n个顶点中,找出 n - 1条连线,使得各边权值最⼩for(int i = 1; i < G.numVertexes; i++) {min = INF, j = 1, k = 0;//寻找 V-U到 U的最⼩权值minfor(j; j < G.numVertexes; j++) {// lowcost[j] != 0保证顶点在 V-U中,⽤k记录此时的最⼩权值边在 V-U中顶点的下标if(lowcost[j] != 0 && lowcost[j] < min) {min = lowcost[j];k = j;}}}printf("V[%d]-V[%d] weight = %d\n", arjvex[k], k, min);lowcost[k] = 0; //表⽰将Vk纳⼊ U//查找邻接矩阵Vk⾏的各个权值,与lowcost的对应值进⾏⽐较,若更⼩则更新lowcost,并将k存⼊arjvex数组中for(int i = 1; i < G.numVertexes; i++) {if(lowcost[i] != 0 && G.arc[k][i] < lowcost[i]) {lowcost[i] = G.arc[k][i];arjvex[i] = k;}}}int main() {MGraph *G = (MGraph *)malloc(sizeof(MGraph));CreateMGraph(G);MiniSpanTree_Prim(*G);}/*input:4 5abcd0 1 20 2 20 3 71 2 42 3 8output:V[0]-V[1] weight = 2V[0]-V[2] weight = 2V[0]-V[3] weight = 7最⼩总权值: 11*/时间复杂度O(n^2)克鲁斯卡尔算法(Kruskal算法)思路:以边为⽬标进⾏构建最⼩⽣成树在边集中依次寻找最⼩权值边,若构建是不形成环路(利⽤parent数组记录各点的连通分量),则将其添加到最⼩⽣成树中。

图论论文

图论论文

最小生成树——Prim算法1、算法问题的提出首先介绍生成树的概念连通图G=(V,E)是无向带权图,若一个子图G’是一棵包含G的所有顶点的树,则该子图G’称为G的生成树。

生成树是连通图的极小连通子图。

所谓极小是指:若在树中任意增加一条边,则将出现一个回路;若去掉一条边,将会使之变成非连通图。

生成树各边的权值总和称为生成树的权。

本次设计是求在图G中所有生成树中权值总和(费用/代价)最小的生成树,即最小生成树。

用两个例子进行实例演示。

2、Prim算法思想用哲学的观点来说,每个事物都有自己特有的性质,那么图的最小生成树也是不例外的。

按照生成树的定义,n 个顶点的连通网络的生成树有n 个顶点、n-1 条边。

(1)从树中某一个顶点V0开始,将V0到其他顶点的所有边当作候选边。

(2)重复以下步骤n-1次,使得其他n-1个顶点被并入到生成树中。

○1从候选边挑出权值最小的边输出,并将与该边另一端的相接的顶点V并入生成树中。

○2考察所有剩余顶点V i,如果(V,V i)的权值比lowcost[V i]小,则用(V,V i)的权值更新lowcost[V i]。

其中的vset[i]的值记录顶点V[i]顶点是否被选入最小生成树中,V[i]=0,表示为被选入,V[i]=1,表示已被选入。

用到辅助数组pre[],记录当前所选入顶点的前驱结点,当并入前一个顶点时,剩下顶点到生成树的权值发生了改变时,就需要及时修改剩下顶点V[i]的前驱结点。

3、程序设计(1)所用数据结构,图的存储结构模块(nodetype.h)#define MAXSIZE 7#define INF 100typedef struct{int no;}VertexType; //顶点类型定义typedef struct{int edges[MAXSIZE][MAXSIZE]; //存入边的权值int n; //顶点数int e; //总的边数VertexType vex[MAXSIZE];}MGraph; //图的存储结构MGraph g;(2)主模块(main.cpp)#include#include"nodetype.h"#include"initiate.h"#include"prim.h"void prim(MGraph g,int v0,int &sum);int main(){int sum=0;int v0;initiate(g); //图的初始化printf("请输入起点编号:\n");scanf("%d",&v0);//输入起始节点prim(g,v0,sum); //调用prim算法,构成最小生成树printf("最小生成树的总代价为%d\n",sum);return 0;}(3)读取数据模块,图的初始化(initiate.h)void initiate(MGraph &g){int i,j,v0=0;printf("Please input the Sumnum of MGraph:\n");scanf("%d",&g.n);printf("依次输入各边权值(不相临接的边权值为100)!\n\n"); for(i=1;i<=g.n;i++){g.vex[i].no=i; //节点编号for(j=1;j<=g.n;j++){printf("边[%d][%d]的权值为:",i,j);//各边的权值scanf("%d",&g.edges[i][j]);printf("\n");}}}(4)运用贪心策略——Prim算法构造最小生成树(prim.h)void prim(MGraph g,int v0,int &sum){int lowcost[MAXSIZE],vset[MAXSIZE];int v,pre[MAXSIZE]; //pre[]存入前驱结点数组int i,j,k,min;v=v0; //初始起点for(i=1;i<=g.n;i++){lowcost[i]=g.edges[v0][i]; //lowcost[]的数组pre[i]=v0;vset[i]=0;}vset[v0]=1;sum=0;for(i=1;imin=INF;for(j=1;j<=g.n;j++){if(vset[j]==0&&lowcost[j]min=lowcost[j];k=j;}}vset[k]=1; //将此结点并入到所够造的生成树中v=k;if(min!=INF){printf("边的起点为:%d 终点为:%d 权值为%d\n",pre[v],v,min);sum+=min;}else{break;}for(j=1;j<=g.n;j++){//并入新结点后修改剩下的结点到生成树的权值if(vset[j]==0&&g.edges[v][j]lowcost[j]=g.edges[v][j];pre[j]=v; //并记其下全趋结点}}}}4、算法分析Prim算法的时间复杂度主要是在双重循环构造最小生成树的过程中,设图的顶点数为n,则双重循环的时间复杂度为O(n2),在生成最小生成树的过程中,增加了两个数组,vset[]和lowcost[]数组,同时增加了一个前驱数组prey[],用来记录所选顶点的全趋结点,故空间复杂度为O(3n)。

prim算法c语言

prim算法c语言

prim算法c语言什么是Prim算法?Prim算法,也叫普里姆算法,是一种用于求解最小生成树的贪心算法。

最小生成树是指在一个无向连通图中,连接所有节点且边权值之和最小的树。

Prim算法的基本思想是从一个起始节点开始,每次选择与当前已经构建好的部分形成的子图相连的、权值最小的边所连接的节点,并将该节点加入到已经构建好的部分中。

直到所有节点都被加入到已经构建好的部分中,此时得到了一棵最小生成树。

Prim算法步骤1. 选定一个起点作为已经构建好的部分。

2. 将与该起点相连且未被访问过的边加入到候选集合中。

3. 从候选集合中选择一条权值最小的边连接到未被访问过的节点,并将该节点加入到已经构建好的部分中。

4. 将新加入节点所连接且未被访问过的边加入到候选集合中。

5. 重复步骤3和步骤4,直至所有节点都被加入到已经构建好的部分中。

Prim算法C语言实现下面给出Prim算法C语言实现代码:```#include <stdio.h>#include <stdlib.h>#include <limits.h>#define MAX_VERTICES 100#define INF INT_MAXtypedef struct {int weight;int visited;} Vertex;typedef struct {int vertices[MAX_VERTICES][MAX_VERTICES]; int num_vertices;} Graph;void init_graph(Graph *graph, int num_vertices) {graph->num_vertices = num_vertices;for (int i = 0; i < num_vertices; i++) {for (int j = 0; j < num_vertices; j++) {graph->vertices[i][j] = INF;}}}void add_edge(Graph *graph, int u, int v, int weight) { graph->vertices[u][v] = weight;graph->vertices[v][u] = weight;}void prim(Graph *graph) {Vertex vertices[MAX_VERTICES];for (int i = 0; i < graph->num_vertices; i++) {vertices[i].weight = INF;vertices[i].visited = 0;}vertices[0].weight = 0;for (int i = 0; i < graph->num_vertices - 1; i++) {// 找到未访问过的权值最小的节点int min_vertex_index = -1;for (int j = 0; j < graph->num_vertices; j++) {if (!vertices[j].visited && (min_vertex_index == -1 || vertices[j].weight < vertices[min_vertex_index].weight)) { min_vertex_index = j;}}// 将该节点标记为已访问vertices[min_vertex_index].visited = 1;// 更新与该节点相连的未访问过的节点的权值for (int j = 0; j < graph->num_vertices; j++) {if (!vertices[j].visited && graph->vertices[min_vertex_index][j] < vertices[j].weight) {vertices[j].weight = graph->vertices[min_vertex_index][j];}}}// 输出最小生成树printf("Minimum Spanning Tree:\n");for (int i = 1; i < graph->num_vertices; i++) {printf("%d - %d (%d)\n", i, (i - 1), vertices[i].weight); }}int main() {Graph graph;init_graph(&graph, 6);add_edge(&graph, 0, 1, 6);add_edge(&graph, 0, 2, 1);add_edge(&graph, 0, 3, 5);add_edge(&graph, 1, 4, 3);add_edge(&graph, 2, 4, 5);add_edge(&graph, 2, 3, 5);add_edge(&graph, 2, 5, 4);add_edge(&graph, 3 ,5 ,2);prim(&graph);return EXIT_SUCCESS;}```代码解释- 定义了Vertex结构体,用于存储节点的权值和访问状态。

离散数学大作业——编程实现最小生成树

离散数学大作业——编程实现最小生成树

离散数学大作业——编程实现最小生成树学院:电子工程学院班级:021051学号:*********名:***一、最小生成树概念:设G=(V,E)是无向连通带权图,即一个网络。

E中每条边(v,w)的权为c[v,w]。

所有生成树G’上各边权的总和最小的生成树称为G的最小生成树。

二、prim算法(贪心思想)设图G =(V,E),其生成树的顶点集合为U。

1.把v0放入U。

2.在所有u∈U,v∈V-U的边(u,v)∈E中找一条最小权值的边,加入生成树。

3.把2找到的边的v加入U集合。

如果U集合已有n个元素,则结束,否则继续执行2其算法的时间复杂度为O(n^2)三、程序源代码# include<stdio.h># include<malloc.h># define m 6# define n 11 typedef struct {int i,tag;char s;}vertice;typedef struct {int a,b,tag;int weight;}edge;vertice v[m];edge e[n];void inititate();void sort();void chuli();int biaoji( edge *s); void print();void main() {inititate();sort();chuli();print();}void inititate() {int i;printf("输入图的%d个顶点:\n",m);for(i=0;i<m;i++) {v[i].i=i+1;v[i].tag=0;scanf("%c",&v[i].s);getchar();}printf("\n输入%d条边的两端顶点及权:\n",n);for(i=0;i<n;i++) {scanf("%d %d %d",&e[i].a,&e[i].b,&e[i].weight);e[i].tag=0;}}int biaoji( edge *s) {int i,j;i=s->a;j=s->b;if(v[i].tag==0 || v[j].tag==0) {v[i].tag=1;v[i].tag=1;s->tag=1;return 1;}return 0;}void print() {int i,j=0;printf("\n最小生成树的边为:\n");for(i=0;i<n&&j<m-1;i++)if(e[i].tag==1) {printf("<%d-%d> ",e[i].a,e[i].b);j++;}printf("\n\n");}void sort() {edge s;int i,j;for(i=0;i<n-1;i++) {for(j=i+1;j<n;j++) {if(e[i].weight>e[j].weight) {s=e[i];e[i]=e[j];e[j]=s;}}}}void chuli() {int i,j=0;edge *s;for(i=0;i<n&&j<m;i++) {s=&e[i];if(biaoji(s)==1)j++;}}四、实验结果输入图的6个顶点:1 2 3 4 5 6输入11条边的权及两端顶点:1 2 11 4 61 6 91 3 112 3 22 4 33 5 83 6 74 5 104 6 45 6 5最小生成树的边为:<1-2> <2-3> <2-4> <4-6> <5-6> Press any key to continue。

Prim算法求无向图的最小生成树

Prim算法求无向图的最小生成树
{
v1=j;
v2=k;
min=G[j][k];
}
if(!in[v2])
{
path[i][0]=v1;
path[i][1]=v2;
in[v1]=1;
in[v2]=1;
min=maxright;
}
}
if(!status)
printf("We cannot deal with it because the graph is not connected!\n");
else
{
for(i=0;i<num-1;i++)
printf("Path %d:vertex %d to vertex %d\n",i+1,path[i][0]+1,path[i][1]+1);
}
return 1;
}
本文来自:中国自学编程网(详细出处参考://7402.html
下面是源代码:
/* Prim.c
Copyright (c) 2002, 2006 by ctu_85
All Rights Reserved.
*/
/* The impact of the situation of articulation point exists can be omitted in Prim algorithm but not in Kruskal algorithm */
restart:
printf("Please enter the number of vertex(s) in the graph:\n");
scanf("%d",&num);

最小生成树

最小生成树
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个顶点"到"该顶点"的权值。

Prim算法和Kruskal算法

Prim算法和Kruskal算法

Prim算法和Kruskal算法Prim算法和Kruskal算法都能从连通图找出最小生成树。

区别在于Prim算法是挨个找,而Kruskal是先排序再找。

一、Prim算法:Prim算法实现的是找出一个有权重连通图中的最小生成树,即:具有最小权重且连接到所有结点的树。

(强调的是树,树是没有回路的)。

Prim算法是这样来做的:首先以一个结点作为最小生成树的初始结点,然后以迭代的方式找出与最小生成树中各结点权重最小边,并加入到最小生成树中。

加入之后如果产生回路则跳过这条边,选择下一个结点。

当所有结点都加入到最小生成树中之后,就找出了连通图中的最小生成树了。

Prim算法最小生成树查找过程:注意:若候选轻边集中的轻边不止一条,可任选其中的一条扩充到T中。

连通网的最小生成树不一定是惟一的,但它们的权相等。

【例】在上图(e)中,若选取的轻边是(2,4)而不是(2,1)时,则得到如图(h)所示的另一棵MST。

算法特点该算法的特点是当前形成的集合T始终是一棵树。

将T中U和TE分别看作红点和红边集,V-U看作蓝点集。

算法的每一步均是在连接红、蓝点集的紫边中选择一条轻边扩充进T中。

MST性质保证了此边是安全的。

T从任意的根r开始,并逐渐生长直至U=V,即T 包含了C中所有的顶点为止。

MST性质确保此时的T是G的一棵MST。

因为每次添加的边是使树中的权尽可能小,因此这是一种"贪心"的策略。

算法分析该算法的时间复杂度为O(n2)。

与图中边数无关,该算法适合于稠密图。

算法演示:/sjjg/DataStructure/DS/web/flashhtml/prim.htm二、Kruskal算法:Kruskal算法与Prim算法的不同之处在于,Kruskal在找最小生成树结点之前,需要对所有权重边做从小到大排序。

将排序好的权重边依次加入到最小生成树中,如果加入时产生回路就跳过这条边,加入下一条边。

当所有结点都加入到最小生成树中之后,就找出了最小生成树。

prim算法模板

prim算法模板

prim算法模板详解
Prim算法是一种贪婪算法,用于在加权图中找到最小生成树。

Prim算法的基本思想是不断选择距离当前生成树最近的顶点,并将其加入生成树中,直到所有顶点都被加入生成树中。

以下是Prim算法的模板:
1.初始化生成树为任意一个顶点,将其他顶点放入未访问的集合中。

2.不断从未访问的顶点中选择距离当前生成树最近的顶点,并将其加入生成
树中。

3.更新当前生成树中所有顶点的距离,以便在下一次迭代中选择距离更近的
顶点。

4.重复步骤2和3,直到所有顶点都被加入生成树中。

以下是Prim算法的Python实现:
其中,graph是一个二维数组,表示加权图,num_vertices是图中顶点的数量。

selected_vertices是最终生成的生成树的顶点序列。

最小生成树问题

最小生成树问题

最小生成树问题
最小生成树问题是指在连接有n个点的图的所有n-1条边中,找到一棵边权和最小的树,这棵树包含了图中所有的点,并且所有点之间都是通过这些边相互连接的。

最小生成树问题可以用来解决一些实际问题,比如网络规划、电力传输、通信网络等。

在计算机领域中,最小生成树问题通常可以用来解决分布式系统中的数据同步问题、数据中心间的通信问题等。

常用的解决最小生成树问题的算法有Prim算法和Kruskal算法。

Prim算法是一种贪心算法,它从一个初始点开始,每次选择与当前生成树相连的边中权值最小的边,并且将该边连接的点加入到生成树中。

重复这个过程,直到生成树包含了所有的点为止。

Kruskal算法是一种基于并查集的贪心算法。

它将所有边按照权值从小到大排序,然后依次遍历每条边,如果这条边连接的两个点不在同一个连通分量中,则将这条边添加到最小生成树中,并合并这两个连通分量。

重复这个过程,直到生成树包含了所有的点为止。

最小生成树问题是一个经典的优化问题,可以使用上述的两种算法来解决。

其中Prim算法的时间复杂度为O(n^2),Kruskal
算法的时间复杂度为O(m log n),其中n表示点的个数,m表示边的个数。

prim算法和kruskal算法例题

prim算法和kruskal算法例题

一、概述在图论中,prim算法和kruskal算法是两种常用的最小生成树算法。

它们分别以不同的方式来寻找给定图的最小生成树,是解决最小生成树问题的有效方法。

本文将重点介绍prim算法和kruskal算法,并通过例题分析,展示它们的应用及原理。

二、prim算法1. prim算法概述2. prim算法步骤3. 例题分析:通过一个具体图示例,展示prim算法的应用过程,详细阐述每一步的操作及思路。

4. prim算法优缺点三、kruskal算法1. kruskal算法概述2. kruskal算法步骤3. 例题分析:通过一个具体图示例,展示kruskal算法的应用过程,详细阐述每一步的操作及思路。

4. kruskal算法优缺点四、prim算法和kruskal算法的比较1. 时间复杂度2. 空间复杂度3. 适用范围4. 其他特点五、例题分析总结通过对两种算法在具体例题中的应用过程分析,总结prim算法和kruskal算法的异同点,以及在实际问题中应用时的考虑因素。

六、结论根据对prim算法和kruskal算法的介绍及例题分析,总结两种算法的特点和应用场景,为不同情况下的最小生成树问题提供参考指导。

七、参考文献列出本文所参考的相关书籍、文献或全球信息站信息,为读者进一步了解prim算法和kruskal算法提供便利。

八、附录可放置示例代码、补充说明或其他相关内容,以便读者更好地理解prim算法和kruskal算法。

由于当前训练模型对于编程题的掌握有一定限制,可以提供在Prim算法和Kruskal算法方面相关的例题解析和应用案例。

以下是一个基于图的例题及其解析。

假设有一个带权重的无向连通图G,图中的顶点集合为V,边的集合为E,每条边的权重由权重函数w(u, v)给出,其中u, v为边的两个顶点。

现在需要使用Prim算法和Kruskal算法来寻找图G的最小生成树。

首先我们需要给出一个具体的图G,如下所示:顶点集合V = {A, B, C, D, E}边的集合E = {(A, B, 3), (A, C, 1), (A, D, 5), (B, C, 4), (B, D, 6), (B, E, 2), (C, D, 7), (C, E, 8), (D, E, 9)}其中,每个元组表示一条边的起始顶点、终止顶点和权重。

数据结构(三十三)最小生成树(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条边为⽌。

《最小生成树(Prim算法)》算法演示程序设计说明

《最小生成树(Prim算法)》算法演示程序设计说明

《最小生成树(Prim算法)》算法演示程序设计说明040648 范成同济大学2004级计算机4班一、设计要求题目:编写Prim算法的最小生成树程序,输出一个给定无向带权图的最小生成树。

二、设计思想最小生成树的定义:假设一个单位要在n个办公地点之间建立通信网,则连通n个地点只需要n-1条线路。

可以用连通的无向网来表示n个地点以及它们之间可能设置的通信线路,其中网的顶点表示城市,边表示两地间的线路,赋于边的权值表示相应的代价。

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

其中一棵使总的耗费最少,即边的权值之和最小的生成树,称为最小生成树。

构造最小生成树可以用多种算法。

其中多数算法利用了最小生成树的下面一种简称为MST的性质:假设N=(V,{E})是一个连通网,U是顶点集V的一个非空子集。

若(u,v)是一条具有最小权值(代价)的边,其中u∈U,v∈V-U,则必存在一棵包含边 (u.v)的最小生成树。

普里姆(Prim)算法即是利用MST性质构造最小生成树的算法。

算法思想如下:假设N=(V,{E})和是连通网,TE是N上最小生成树中边的集合。

算法从U={u0}( u0∈V),TE={}开始,重复执行下述操作:在所有u∈U,v∈V-U的边(u, v) ∈E中找一条代价最小的边(u0, v0)并入集合TE,同时v0并入U,直到U=V为止。

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

本程序中,采用图的邻接矩阵表示法,并使用二维数组表示网的邻接矩阵。

三、其他相关信息开发平台:Microsoft Visual 2005 Professional程序类型:Win32控制台应用程序开发平台及程序运行截图:。

p算法求最小生成树例题

p算法求最小生成树例题

选择题在Prim算法求解最小生成树的过程中,每次选择的点是?A. 权重最大的点B. 与已生成树连接权重最小的点(正确答案)C. 任意未加入树的点D. 距离已生成树最远的点下列关于Kruskal算法的描述中,哪一项是正确的?A. 每次选择权重最大的边加入生成树B. 形成的生成树一定包含所有顶点(正确答案)C. 只适用于无向图且边权值均为正的情况D. 算法结束时可能形成多个不连通的子图对于一个包含n个顶点的连通图,使用Prim算法构建最小生成树时,需要进行的边选择次数是?A. n-1次(正确答案)B. n次C. n(n-1)/2次D. n2次在使用Kruskal算法构建最小生成树时,如果图中存在环,应该如何处理?A. 忽略该环,继续添加边B. 删除环中权重最大的边(正确答案)C. 删除环中权重最小的边D. 停止算法,图无法生成树Prim算法和Dijkstra算法的主要区别在于?A. 使用的数据结构不同B. Prim用于求最小生成树,Dijkstra用于求最短路径(正确答案)C. 算法的复杂度不同D. 适用的图类型不同在一个加权无向图中,若采用Prim算法从某一顶点开始构建最小生成树,则该顶点?A. 必须是度数最小的顶点B. 可以是图中的任意顶点(正确答案)C. 必须是权重最大的顶点D. 必须是与其他顶点连接边数最多的顶点Kruskal算法在构建最小生成树时,主要依赖于哪种数据结构来避免形成环?A. 栈B. 队列C. 并查集(正确答案)D. 优先级队列下列关于最小生成树(MST)的说法中,哪一项是错误的?A. MST是图中边权值和最小的生成树B. MST是唯一的,不随算法的改变而改变(正确答案)C. MST可以是不唯一的,但权值和一定是最小的D. MST可以通过Prim或Kruskal等算法找到在一个包含n个顶点m条边的无向图中,使用Kruskal算法构建最小生成树的时间复杂度主要取决于?A. 图的顶点数nB. 图的边数mC. 对边进行排序的时间复杂度(正确答案)D. 使用的并查集操作的时间复杂度。

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

xx学院《数据结构与算法》课程设计报告书课程设计题目 PRIM算法求最小生成树院系名称计算机科学与技术系专业(班级)姓名(学号)指导教师完成时间一、问题分析和任务定义在该部分中主要包括两个方面:问题分析和任务定义;1 问题分析本次课程设计是通过PRIM(普里姆)算法,实现通过任意给定网和起点,将该网所对应的所有生成树求解出来。

在实现该本设计功能之前,必须弄清以下三个问题:1.1 关于图、网的一些基本概念1.1.1 图图G由两个集合V和E组成,记为G=(V,E),其中V是顶点的有穷非空集合,E是V中顶点偶对的有穷集,这些顶点偶对称为边。

通常,V(G)和E(G)分别表示图G的顶点集合和边集合。

E(G)也可以为空集。

则图G只有顶点而没有边。

1.1.2 无向图对于一个图G,若边集E(G)为无向边的集合,则称该图为无向图。

1.1.3 子图设有两个图G=(V,E)G’=(V’,),若V’是V的子集,即V’⊆V ,且E’是E的子集,即E’⊆E,称G’是G的子图。

1.1.4 连通图若图G中任意两个顶点都连通,则称G为连通图。

1.1.5 权和网在一个图中,每条边可以标上具有某种含义的数值,该数值称为该边的权。

把边上带权的图称为网。

如图1所示。

1.2 理解生成树和最小生成树之间的区别和联系1.2.1 生成树在一个连通图G中,如果取它的全部顶点和一部分边构成一个子图G’,即:V(G’)= V(G)和E(G’)⊆E(G),若边集E(G’)中的边既将图中的所有顶点连通又不形成回路,则称子图G’是原图G的一棵生成树。

1.2.2 最小生成树图的生成树不是唯一的,把具有权最小的生成树称为图G的最小生成树,即生成树中每条边上的权值之和达到最小。

如图1所示。

图1.网转化为最小生成树1.3 理解PRIM(普里姆)算法的基本思想1.3.1 PRIM算法(普里姆算法)的基本思想假设G =(V,E)是一个具有n个顶点的连通网,T=(U,TE)是G的最小生成树,其中U是T的顶点集,TE是T的边集,U和TE的初值均为空集。

算法开始时,首先从V中任取一个顶点(假定取V0),将它并入U中,此时U={V0},然后只要U是V的真子集,就从那些其一个端点已在T中,另一个端点仍在T外的所有边中,找一条最短(即权值最小)边,假定为(i,j),其中V i∈U,V j∈(V-U),并把该边(i,j)和顶点j分别并入T的边集TE和顶点集U,如此进行下去,每次往生成树里并入一个顶点和一条边,直到n-1次后就把所有n个顶点都并入到生成树T的顶点集中,此时U=V,TE中含有n-1条边,T就是最后得到的最小生成树。

可以看出,在普利姆算法中,是采用逐步增加U中的顶点,常称为“加点法”。

为了实现这个算法在本设计中需要设置一个辅助数组closedge[ ],以记录从U到V-U具有最小代价的边。

当辅助数组中存在两个或两个以上的最小代价的边时,此时最小生成树的形态就不唯一,此时可以在程序中采用递归的算法思想求出每个最小生成树。

1.3.2 在PRIM算法中需要解决的两个问题①在无向网中,当出现从一个顶点到其它顶点时,若其边上的权值相等,则可能从某一起点出发时会得出不同的最小生成树,但最小生成树的权必定都相等,此时我们应该怎样将所有的最小生成树求解出来;②每次如何从生成树T中到T外的所有边中,找出一条最短边。

例如,在第k次(1≤k ≤n-1)前,生成树T中已有k个顶点和k-1条边,此时T中到T外的所有边数为k(n-k),当然它也包括两顶点间没有直接边相联,其权值被看做常量INFINIT的边在内,从如此多的边中查找最短边,其时间复杂度为O(k(n-k)),显然是很费时的,是否有一种好的方法能够降低查找最短边的时间复杂度。

1.3.3 上述问题的解决方法针对①中出现的问题可以通过在算法中实现,详情请看PRIM算法的基本思想;针对②中出现的问题,通过对PRIM算法的分析,可以使查找最短边的时间复杂度降低到O(n-k)。

具体方法是假定在进行第k次前已经保留着从T中到T外的每一顶点(共n-k 个顶点)的各一条最短边,进行第k次时,首先从这n-k条最短边中,找出一条最最短的边,它就是从T中到T外的所有边中的最短边,假设为(i,j),此步需进行n-k次比较;然后把边(i,j)和顶点j分别并入T中的边集TE和顶点集U中,此时T外只有n-(k+1)个顶点,对于其中的每个顶点t,若(j,t)边上的权值小于已保留的从原T中到顶点t的最短边的权值,则用(j,t)修改之,使从T中到T外顶点t的最短边为(j,t),否则原有最短边保持不变,这样,就把第k次后从T中到T外每一顶点t的各一条最短边都保留下来了,为进行第k+1次运算做好了准备,此步需进行n-k-1次比较。

所以,利用此方法求第k次的最短边共需比较2(n-k)-1次,即时间复杂度为O(n-k)。

2 任务定义通过以上的分析和理解,对本设计分别从以下三个方面进行分析:2.1 输入数据的类型在本次设计中,是对无向网进行操作,网中的顶点数、边数、顶点的编号及每条边的权值均通过键盘输入,它们的数据类型均为整型,其中,权值的范围为0~32768(即“∞”);2.2 输出数据的类型当用户将无向网创建成功以后,就可以通过键盘任意输入一个起点值将其对应的最小生成树的生成路径及权值显示出来;2.3测试数据本次设计中是通过用PRIM算法求最小生成树,分别用以下三组数据进行测试:2.3.1 假定在创建无向网时只输入一个顶点,如图2所示,验证运行结果1图2.单一节点的图2.3.2 假定创建的无向网如图3所示,验证运行结果2.3.3 假定创建的无向网如图4所示,验证运行结果二、概要设计和数据结构的选择在本次设计中,网的定义为G=(V,E),V 表示非空顶点集,E 表示边集,其存储结构这里采用邻接矩阵的方式来存储。

1 数据类型的定义在本设计中所涉及到的数据类型定义如下:1.1 符号常量的定义算法中涉及到两个符号常量,定义如下:#define MAX 20 功能:用于表示网中最多顶点个数;#define INFINIT 32768 功能:用于表示权的最大值,即∞。

1.2 结构体的定义整个算法中涉及的结构体定义如下:1.2.1 定义结构体ArcNode功能:用于存放边的权值typedef struct{int adj;//权值}ArcNode; 4 3 2 6 5 666 5 5 5 2 13 41 图3.网中存在权值相等的边 4 132 6 5 16 19 187 5 6 211433 11 图4.网中边的权值各不相等1.2.2定义结构体AdjMatrix功能:用于表示网的结构及存储方式。

typedef struct{int vexs[MAX];//vexs表示顶点向量int vexnum,arcnum;//分别表示图的顶点数和弧数*/ArcNode arcs[MAX][MAX];//邻接矩阵}AdjMatrix;1.2.3 定义结构体Node功能:用于表示求最小生成树时用到的辅助数组。

typedef struct{int adjvex;//存放顶点编号int lowcost;//存放顶点权值}Node;1.3 外部变量的定义算法中涉及到两个外部变量,定义如下:Node close[MAX] 功能:存放每个顶点所连接的边所对应的权值;int flag=0 功能:标志变量,当创建网时,输入的顶点个数<=1时,直接输出“不存在最小生成树”程序不在往后执行,否则继续往下执行。

2 概要设计在本设计中,主要包括六个函数模块,其算法思想和功能描述如下:2.1 子函数int Locate(AdjMatrix *G,int V)功能:是求出某个顶点在顶点向量表中的位置,在其函数体中通过for循环将某一顶点与顶点向量表中的所有顶点进行比较,当出现两者相等时,将该顶点在vexs[MAX]数组中的下标通过return语句返回,否则返回-1;2.2 子函数AdjMatrix *creat(AdjMatrix *G)功能:是完成无向网的创建,在其函数体中,首先通过键盘输入网中顶点数,若顶点个数<=1时,将标志变量flag置为1并显示“最小生成树不存在”,然后返回主调函数;否则,继续通过键盘输入网中的边数,通过二重for循环初始化邻接矩阵,然后输入各个顶点的编号及每条边的权值,调用函数Locate()求出每条边所对应的两个顶点在顶点向量表中的位置后,将对应在邻接矩阵中的相应位置赋予权值,从而在邻接矩阵中存放了相关连的顶点之间边的权值,完成无向网的存储;2.3 子函数int Minium(AdjMatrix *G,Node close[])功能:是求出辅助数组close[]中权值最小的边,在其函数体中,首先将最小权值(min)置为INFINIT(∞),通过for循环将辅助数组中的各条边的权值与min进行比较,最终使得min中存放的是当前数组close[]中最小的权值,并将其在辅助数组中的相应位置返回主调函数中;2.4 子函数void prim(AdjMatrix *G,int u)功能:是利用PRIM(普里姆)算法求出无向网所对应的最小生成树,在其函数体中,首先,定义AdjMatrix *p用于存放最小生成树中的顶点(按生成最小生成树时的顺序存放),调用函数Locate()求出起点u在顶点向量表中的位置,将u存放到p的顶点向量表中,初始化初始化U={u},利用for循环对V-U中的顶点i,初始化close[i],再利用for循环找n-1条边(n=G->vexnum),其中,调用函数Minium()求出辅助数组close[]中权值最小的边, 并将其在辅助数组中的相应位置返回到主调函数中并赋给k0,此时close[k0]中存有当前最小边(u0, v0)的信息,将边所对应的终点放入p的顶点向量表中,累加边的权值;然后,输出<起点终点权值>;最后,在顶点v0并入U之后,利用for循环更新closedge[i];当所有的顶点v0都纳入U集合之后,输出最小生成树中的顶点序列及最小生成树的权值之和。

2.5 子函数void display(AdjMatrix *G)功能:是创建的无向网所对应的邻接矩阵;2.6 主函数void main()功能:是完成对上述各子函数的调用,完成本次设计的要求,在其函数体中,通过while 循环可以实现重复创建网以及可以从网中的不同顶点出发求出对应的最小生成树。

3程序流程图通过上述概要设计,各程序模块之间的层次(调用)关系,用流程图表示如下:上述流程图反映了整个算法中各个子函数之间的嵌套调用,从主函数开始顺序往下执行,首先调用creat()函数创建无向网并采用邻接矩阵的方式来存储;然后将该网对应的邻接矩阵通过调用display()函数输出;最后调用prim ()函数求出该网所对应的最小生成树,并且在prim ()函数中通过嵌套调用Minium ()函数求出辅助数组close[]中权值最小的边,从而完成了本设计的要求。

相关文档
最新文档