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); } }