Prime算法求最小生成树 (邻接矩阵)

/*

Name: Prime算法求最小生成树 (邻接矩阵)

Copyright:

Author: 巧若拙

Date: 25/11/14 13:38

Description:

实现了 Prime算法求最小生成树 (邻接矩阵)的普通算法和最小堆优化算法。

*/

#include<stdio.h>

#include<stdlib.h>

#define MAX 2000   //最大顶点数量

#define INFINITY 999999   //无穷大

typedef struct MinHeap{

int num;//存储顶点序号

int w;  //存储顶点到最小生成树的距离

} MinHeap; //最小堆结构体

int map[MAX][MAX] = {0};//邻接矩阵存储图信息

void CreatGraph(int m, int n);//创建邻接矩阵图

void CreatGraph_2(int m, int n);//创建邻接矩阵图(随机图)

void PrintGraph(int n);//输出图

void Prime(int n, int v0);//Prime算法求最小生成树(原始版本)

void Prime_MinHeap(int n, int v0);//Prime算法求最小生成树(优先队列版本)

void BuildMinHeap(MinHeap que[], int n);

void MinHeapSiftDown(MinHeap que[], int n, int pos);

void MinHeapSiftUp(MinHeap que[], int n, int pos);

void ChangeKey(MinHeap que[], int pos, int weight);//将第pos个元素的关键字值改为weight

int SearchKey(MinHeap que[], int pos, int weight);//查找最小堆中关键字值为k的元素下标,未找到则返回-1(非递归)

int ExtractMin(MinHeap que[]);//删除并返回最小堆中具有最小关键字的元素

int main()

{

int i, j, m, n, v0 = 0;

printf("请输入顶点数量:");

scanf("%d", &n);

printf("\n请输入边数量:");

scanf("%d", &m);

CreatGraph_2(m, n);//创建邻接矩阵图

// PrintGraph(n);//输出图

Prime(n, v0);//Prime算法求最小生成树(原始版本)

Prime_MinHeap(n, v0);//Prime算法求最小生成树(优先队列版本)

return 0;

}

void CreatGraph(int m, int n)//创建邻接矩阵图

{

int i, j, a, b, c;

for (i=0; i<n; i++) //初始化顶点数据

{

for (j=0; j<n; j++)

{

map[i][j] = (i == j) ? 0 : INFINITY;

}

}

printf("\n请按照a b c格式输入边信息:\n");

for (i=0; i<m; i++)

{

scanf("%d%d%d", &a,&b,&c);

map[a][b] = map[b][a] = c;

}

}

void CreatGraph_2(int m, int n)//创建邻接矩阵图(随机图)

{

int i, j, a, b, c;

for (i=0; i<n; i++) //初始化顶点数据

{

for (j=0; j<n; j++)

{

map[i][j] = (i == j) ? 0 : INFINITY;

}

}

for (i=1; i<n; i++)//确保是连通图

{

map[i][0] = map[0][i] =  rand() % 100 + 1;

}

m -= n - 1;

while (m > 0)

{

for (i=0; i<n; i++)

{

for (j=i+1; j<n; j++)

{

if (rand()%10 == 0) //有10%的概率出现边

{

if (map[j][i] == INFINITY)

{

map[i][j] = map[j][i] =  rand() % 100 + 1;

m--;

if (m == 0)

return;

}

}

}

}

}

}

void PrintGraph(int n)//输出图

{

int i, j;

for (i=0; i<n; i++)

{

printf("G[%d] = %d: ", i, i);

for (j=0; j<n; j++)

{

if (map[i][j] != 0 && map[i][j] != INFINITY)

printf("<%d, %d> = %d", i, j, map[i][j]);

}

printf("\n");

}

printf("\n");

}

void Prime(int n, int v0)//Prime算法求最小生成树(原始版本)

{

int book[MAX] = {0}; //标记该顶点是否已经在路径中

int dic[MAX] = {0}; //存储顶点到最小生成树的距离

int adj[MAX] = {0}; //存储顶点在最小生成树树中的邻接点序号

int min, i, j, k;

for (i=0; i<n; i++) //初始化工作

{

dic[i] = map[v0][i];

adj[i] = v0;

}

book[v0] = 1;

for (i=1; i<n; i++) //每趟确定一个新顶点,共n-1趟

{

min = INFINITY;

k = v0;

for (j=0; j<n; j++)//找出离最小生成树最近的顶点k

{

if (book[j] == 0 && dic[j] < min)

{

min = dic[j];

k = j;

}

}

book[k] = 1;  
printf("<%d, %d> = %d   ", adj[k], k, dic[k]);

for (j=0; j<n; j++)//更新与顶点k的邻接点的dic[]值

{

if (book[j] == 0 && dic[j] > map[k][j])

{

dic[j] = map[k][j];

adj[j] = k;

}

}

}

min = 0;

for (i=0; i<n; i++) //输出各顶点在最小生成树中的邻接点及边的长度

{

// printf("<%d, %d> = %d\n", adj[i], i, dic[i]);

min += dic[i];

}

printf("最小生成树总长度(权值)为 %d\n", min);

}

void Prime_MinHeap(int n, int v0)//Prime算法求最小生成树(优先队列版本)

{

int book[MAX] = {0}; //标记该城市是否已经在路径中

int dic[MAX] = {0}; //存储顶点到最小生成树的距离

int adj[MAX] = {0}; //存储顶点在最小生成树树中的邻接点序号

MinHeap que[MAX+1];//最小堆用来存储顶点序号和到最小生成树的距离

int min, i, j, k, pos, K;

que[0].num = n; //存储最小堆中的顶点数量

for (i=0; i<n; i++) //初始化工作

{

dic[i] = map[v0][i];

adj[i] = v0;

que[i+1].num = i;

que[i+1].w = dic[i];

}

book[v0] = 1;

BuildMinHeap(que, n);//构造一个最小堆

ExtractMin(que);//删除顶点v0

for (i=1; i<n; i++) //每趟确定一个新顶点,共n-1趟

{

k = ExtractMin(que);//删除并返回最小堆中具有最小关键字的元素

book[k] = 1;   printf("<%d, %d> = %d   ", adj[k], k, dic[k]);

for (j=0; j<n; j++)//更新与顶点k的邻接点的dic[]值,同时更新最小堆

{

if (book[j] == 0 && dic[j] > map[k][j])

{

pos = SearchKey(que, j, dic[j]);

dic[j] = map[k][j];

adj[j] = k;

ChangeKey(que, pos, dic[j]);//将第pos个元素的关键字值改为weight

}

}

}

min = 0;

for (i=0; i<n; i++) //输出各顶点在最小生成树中的邻接点及边的长度

{

// printf("<%d, %d> = %d\n", adj[i], i, dic[i]);

min += dic[i];

}

printf("最小生成树总长度(权值)为 %d\n", min);

}

int ExtractMin(MinHeap que[])//删除并返回最小堆中具有最小关键字的元素

{

int pos = que[1].num;

que[1] = que[que[0].num--];

MinHeapSiftDown(que, que[0].num, 1);

return pos;

}

int SearchKey(MinHeap que[], int pos, int weight)//查找最小堆中关键字值为k的元素下标,未找到则返回-1(非递归)

{

int Stack[MAX] = {0};

int i = 1, top = -1;

while ((i <= que[0].num && que[i].w <= weight) || top >= 0)//类似先序遍历二叉树的方式查找

{

if (i <= que[0].num && que[i].w <= weight)

{

if (que[i].w == weight && que[i].num == pos)//权值与顶点序号都必须对应

return i;

Stack[++top] = i;//该结点入栈

i *= 2; //搜索左孩子

}

else

{

i = Stack[top--] * 2 + 1; //结点退栈并搜索右孩子

}

}

return -1;

}

void ChangeKey(MinHeap que[], int pos, int weight)//将第pos个元素的关键字值改为weight

{

if (weight < que[pos].w) //关键字值变小,向上调整最小堆

{

que[pos].w = weight;

MinHeapSiftUp(que, que[0].num, pos);

}

else if (weight > que[pos].w)  //关键字值变大,向下调整最小堆

{

que[pos].w = weight;

MinHeapSiftDown(que, que[0].num, pos);

}

}

void MinHeapSiftDown(MinHeap que[], int n, int pos)  //向下调整结点

{

MinHeap temp = que[pos];

int child = pos * 2; //指向左孩子

while (child <= n)

{

if (child < n && que[child].w > que[child+1].w) //有右孩子,且右孩子更小些,定位其右孩子

child += 1;

if (que[child].w < temp.w) //通过向上移动孩子结点值的方式,确保双亲小于孩子

{

que[pos] = que[child];

pos = child;

child = pos * 2;

}

else

break;

}

que[pos] = temp; //将temp向下调整到适当位置

}

void MinHeapSiftUp(MinHeap que[], int n, int pos)  //向上调整结点

{

MinHeap temp = que[pos];

int parent = pos / 2; //指向双亲结点

if (pos > n) //不满足条件的元素下标

return;

while (parent > 0)

{

if (que[parent].w > temp.w) //通过向下移动双亲结点值的方式,确保双亲小于孩子

{

que[pos] = que[parent];

pos = parent;

parent = pos / 2;

}

else

break;

}

que[pos] = temp; //将temp向上调整到适当位置

}

void BuildMinHeap(MinHeap que[], int n)//构造一个最小堆

{

int i;

for (i=n/2; i>0; i--)

{

MinHeapSiftDown(que, n, i);

}

}

时间: 2024-07-29 23:42:54

Prime算法求最小生成树 (邻接矩阵)的相关文章

Prime算法求最小生成树 (邻接表)

/* Name: Prime算法求最小生成树 (邻接表) Copyright: Author: 巧若拙 Date: 25/11/14 13:38 Description: 实现了 Prime算法求最小生成树 (邻接表)的普通算法和最小堆优化算法. */ #include<stdio.h> #include<stdlib.h> #define MAX 2000   //最大顶点数量 #define INFINITY 999999   //无穷大 typedef int VertexT

利用Kruskal算法求最小生成树解决聪明的猴子问题 -- 数据结构

题目:聪明的猴子 链接:https://ac.nowcoder.com/acm/problem/19964 在一个热带雨林中生存着一群猴子,它们以树上的果子为生.昨天下了一场大雨,现在雨过天晴,但整个雨林的地 表还是被大水淹没着,部分植物的树冠露在水面上.猴子不会游泳,但跳跃能力比较强,它们仍然可以在露出水面 的不同树冠上来回穿梭,以找到喜欢吃的果实.现在,在这个地区露出水面的有N棵树,假设每棵树本身的直径都 很小,可以忽略不计.我们在这块区域上建立直角坐标系,则每一棵树的位置由其所对应的坐标表

Prim算法和Kruskal算法求最小生成树

Prim算法 连通分量是指图的一个子图,子图中任意两个顶点之间都是可达的.最小生成树是连通图的一个连通分量,且所有边的权值和最小. 最小生成树中,一个顶点最多与两个顶点邻接:若连通图有n个顶点,则最小生成树中一定有n-1条边. Prim算法需要两个线性表来进行辅助: visited: 标记已经加入生成树的顶点:(它的功能可以由tree取代) 初始状态:生成树根节点为真,其它为0. tree: 记录生成树,tree[x]保存顶点x的直接根节点下标,若x为树的根节点则tree[x]为其自身. 初始状

Kruskal算法求最小生成树

求加权连通图的最小生成树的算法.kruskal算法总共选择n- 1条边,(共n条边)所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中.注意到所选取的边若产生环路则不可能形成一棵生成树.kruskal算法分e 步,其中e 是网络中边的数目.按耗费递增的顺序来考虑这e 条边,每次考虑一条边.当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入. 克鲁斯卡尔算法(Kruskal's algorithm)是两个经典的最小生成树算法的

kruskal算法求最小生成树(jungle roads的kruskal解法)

注意: 注意数组越界问题(提交出现runtimeError代表数组越界) 刚开始提交的时候,边集中边的数目和点集中点的数目用的同一个宏定义,但是宏定义是按照点的最大数定义的,所以提交的时候出现了数组越界问题,以后需要注意啦. Description The Head Elder of the tropical island of Lagrishan has a problem. A burst of foreign aid money was spent on extra roads betwe

最小生成树 克鲁斯卡尔(Kruskal)算法求最小生成树

Kruskal算法的过程: (1) 将全部边按照权值由小到大排序. (2) 按顺序(边权由小到大的顺序)考虑没条边,只要这条边和我们已经选择的边步构成圈,就保留这条边,否则放弃这条边.算法 成功选择(n-1)条边后,形成一个棵最小生成树,当然如果算法无法选择出(n-1)条边,则说明原图不连通. 图中的路径按照权值的大小的排序为 AF 1; BE 4; BD 5; BC 6; DC:10; BF 11; DF 14; AE 16; AB 17; EF 33; 算法的处理过程如下 先选A,F不在一个

Kruskra算法求最小生成树

算法思想: (1)创建图,将每个节点的根节点,标记为-1. (2)对图中的边按权重排序 (3)遍历每条边,获取边两端的根节点 如果两个端点的根节点相等,且均是-1,则代表这是一条孤立的边(同时修改目的节点的父节点设为源节点),把这条边计入生成树 如果两个端点的根节点相等,且不是-1(可能是-2,-3,之类),这说明加入这条边构成环路,不需要任何操作 如果两个端点的根节点不相等,这说明这条边连接了两棵树,(同时修改目的节点根节点的父节点设为源节点),把这条边计入生成树 注:其实我们知道最小生成树,

克鲁斯卡尔算法求最小生成树

只是写一个模板,具体讲解就不讲了,是一个并查集的应用+贪心的思想. 路径压缩还是很有用处的,没有压缩的时候tml了三个,压缩之后明变快了不少,虽然还是那么慢 先说一下我的压缩方法就当学习一下并查集: 1 int Find(int x) 2 { 3 int r=x; 4 while(fa[r]!=r)r=fa[r]; 5 while(x!=r){ 6 x=fa[x]; 7 fa[x]=r; 8 } 9 return fa[x]; 10 } 非递归的路径压缩,先找到祖先结点,然后从头到尾的更新路径的

普里姆算法求最小生成树

#include "iostream" using namespace std; const int num = 9; //节点个数 #define Infinity 65535; //本例中以节点0作为生成树的起始节点 void MinSpanTree_Prime(int graphic[num][num]){ int lowcost[num]; //记录从节点num到生成树的最短距离,如果为0则表示该节点已在生成树中 int adjvex[num]; //记录相关节点,如:adjv