C数据结构与算法——无向图(最小生成树) 应用

实验任务

(1) 掌握Kruskal最小生成树算法;
(2) 掌握Prim最小生成树算法。

实验内容

(1) 随机生成一个无向网 G = ( V, E ),V = { A, B, C, D, E, F },| E | = 11,边的权值取值范围为 [ 1, 40 ];
(2) 使用Prim算法求出图G的最小生成树,给出选择顶点的顺序;
(3) 使用Kruskal算法从顶点A出发求图G的最小生成树,给出算法添加边的顺序;
(4) 给出最小生成树的代价。

实验源码

#include 
#include 
#include 
#include 
#include "math.h"

#define MAX_AMNUMS 100

int cost = 0; // 代价

// 枚举 布尔
typedef enum {
    FALSE,
    TRUE
} Boolean;

// 队列
typedef struct {
    int *base;
    int front;
    int rear;
} SqQueue;

// 无向网
typedef struct {
    char verxs[MAX_AMNUMS];
    int arcs[MAX_AMNUMS][MAX_AMNUMS];
    int numVertexes, numEdges;
    int minW, maxW;
} AMGraph;

// 访问标志
Boolean visited[MAX_AMNUMS];

// 克鲁斯卡尔
struct { // 辅助数组 Edge(边集) 的定义
    char Head; // 边的始点
    char Tail; // 边的终点
    int lowCost; // 边上的权值
} Edge[MAX_AMNUMS];

// 辅助数组 VexSet 的定义
int VexSet[MAX_AMNUMS];

Boolean InitUDN(AMGraph *G); // 初始化
void CreateRandUDN(AMGraph *G); // 创建随机无向网
void knuthShuffle(int arr[], int length); // 洗牌算法
void swapInt(int *card_1, int *card_2); // 交换函数
void PrintUDN(AMGraph G); // 打印无向网
void MiniSpanTree_Prim(AMGraph G, char vex); // 普里姆最小生成树算法
void MiniSpanTree_Kruskal(AMGraph G); // 克鲁斯卡尔最小生成树算法
void BubbleSort(int length); // 冒泡排序
int LocateVex(AMGraph G, char vex); // 取出顶点信息对应的顶点下标
void PrintCost(); // 打印代价

/**
 * 

无向网 实验三

* @return 0 */
int main() { srand(time(NULL)); // 定义 AMGraph G; // 初始化 InitUDN(&G); // 顶点数+边数 G.numVertexes = 7; G.numEdges = 11; // 权值范围 G.minW = 1; G.maxW = 40; // 创建随机无向网 CreateRandUDN(&G); // 打印无向网 PrintUDN(G); printf("\n"); // Prim算法 printf("\n使用Prim算法从顶点A出发求G的最小生成树,依次选择的顶点是:\n"); MiniSpanTree_Prim(G, 'A'); printf("\n"); // Kruskal算法 printf("\n使用Kruskal算法求G的最小生成树,依次选择的边是:\n"); MiniSpanTree_Kruskal(G); printf("\n"); // 最小生成树代价 printf("\n图G最小生成树的代价为:"); PrintCost(); getchar(); } // 初始化无向网 Boolean InitUDN(AMGraph *G) { G = (AMGraph *) malloc(sizeof(AMGraph)); if (!G) { return FALSE; } return TRUE; } void CreateRandUDN(AMGraph *G) { // 顶点编号(字母) for (int i = 0; i < G->numVertexes; i++) { G->verxs[i] = 'A' + i; } // 初始化 邻接矩阵为 相对[minW-maxW]范围无穷大 for (int i = 0; i < G->numVertexes; i++) { for (int j = 0; j < G->numVertexes; j++) { G->arcs[i][j] = (G->maxW) + 1; } } /* * 随机生成 无向网上三角部分 */ /* // 结果测试 int arr[6][6] = { {41, 9, 41, 41, 17, 13}, {9, 41, 19, 16, 7, 41}, {41, 19, 41, 32, 41, 12}, {41, 16, 32, 41, 34, 14}, {17, 7, 41, 34, 41, 22}, {13, 41, 12, 14, 22, 41} }; for (int i = 0; i < 6; i++) { for (int j = 0; j < 6; j++) { G->arcs[i][j] = arr[i][j]; } }*/ int upNum = (pow(G->numVertexes, 2) - G->numVertexes) / 2; int randArr[upNum]; // 生成权值 for (int i = 0; i < G->numEdges; i++) { // max-min+1 , min randArr[i] = rand() % (G->maxW) + (G->minW); } // 生成 无权值 for (int i = (G->numEdges); i < upNum; i++) { randArr[i] = (G->maxW) + 1; } // 洗牌 knuthShuffle(randArr, upNum); // 放入无向网(对称赋值) int count = 0; for (int i = 0; i < G->numVertexes - 1; i++) { for (int j = i + 1; j < G->numVertexes; j++) { G->arcs[i][j] = G->arcs[j][i] = randArr[count++]; } } } // 洗牌 void knuthShuffle(int arr[], int length) { for (int i = length - 1; i >= 1; i--) { swapInt(&arr[i], &arr[rand() % (i + 1)]); } } // 交换 void swapInt(int *card_1, int *card_2) { int tCard; tCard = *card_1; *card_1 = *card_2; *card_2 = tCard; } // 输出 void PrintUDN(AMGraph G) { // 其他信息 printf("无向网 G =(V, E)的顶点集 V = {"); for (int i = 0; i < G.numVertexes; i++) { printf(" %c", G.verxs[i]); if (i != (G.numVertexes - 1)) { printf(","); } } printf(" }、边集 E及其权值如下邻接矩阵所示:\n"); // 表头 printf(" _"); for (int i = 0; i < G.numVertexes; i++) { printf(" %c", G.verxs[i]); } printf(" _"); printf("\n"); // 内容 for (int i = 0; i < G.numVertexes; i++) { // 最左侧打印 if (i == (G.numVertexes - 1)) { printf(" %c |_", G.verxs[i]); // 尾行 } else { printf(" %c | ", G.verxs[i]); // 非尾行 } // 中间打印 for (int j = 0; j < G.numVertexes; j++) { if (G.arcs[i][j] > G.maxW) { printf(" ∞"); } else { printf(" %2d", G.arcs[i][j]); } } // 最右侧打印 if (i == (G.numVertexes - 1)) { printf(" _|"); // 尾行 } else { printf(" |"); // 非尾行 } printf("\n"); } } // Prim void MiniSpanTree_Prim(AMGraph G, char vex) { char adjVex[MAX_AMNUMS]; // 最小边在 v 中那个顶点 int lowCost[MAX_AMNUMS]; // 最小边上的权值 int v = LocateVex(G, vex); // v 为顶点vex的下标 /* * 初始化 */ for (int i = 0; i < G.numVertexes; i++) { // 对 VEX-V 的每一个顶点 Vi, 初始化adjVex 和 lowCost if (i != v) { adjVex[i] = vex; // 初始化全部先为 v 的顶点信息 lowCost[i] = G.arcs[v][i]; // 将邻接矩阵第 0行 所有权值 先加入 lowCost } } lowCost[v] = G.minW - 1; // 初始,V = { v } /* * 构造最小生成树 */ for (int i = 1; i < G.numVertexes; i++) { // 选择其余 n-1 个顶点,生成 n-1 条边(n=G.numVertexes) // 求出T的下一个结点:第 k 个顶点,closedge[k]中存有当前最小边 int min = 41; int j = 0; while (j < G.numVertexes) { // 寻找 和 最小边 和 最小边的另一个顶点 if (lowCost[j] != (G.minW - 1) && lowCost[j] < min) { min = lowCost[j]; v = j; // 当前顶点 变为 相邻顶点 } j++; } // 输出 char tVex_L = adjVex[v]; // tVex_L 为最小边的一个顶点,tVex 属于 VEX char tVex_R = G.verxs[v]; // tVex_R 为最小边的另一个顶点,属于 VEX - V // printf("(%c, %c)", tVex_L, tVex_R); if (i == 1) { printf("%c", tVex_L); } printf(" -> %c", tVex_R); // 选择最小边 lowCost[v] = G.minW - 1; // 第 v 个顶点并入 V 集 for (int k = 0; k < G.numVertexes; k++) { if (lowCost[k] != (G.minW - 1) && G.arcs[v][k] < lowCost[k]) { // 新顶点并入 V 后重新选择最小边 adjVex[k] = G.verxs[v]; // 存入新顶点 v 的信息 lowCost[k] = G.arcs[v][k]; } } } } // Kruskal void MiniSpanTree_Kruskal(AMGraph G) { // 取出无向网的权值 到辅助数组 Edge[]中 int length = 0; for (int i = 0; i < G.numVertexes - 1; i++) { for (int j = i + 1; j < G.numVertexes; j++) { if (G.arcs[i][j] <= G.maxW) { Edge[length].Head = G.verxs[i]; Edge[length].Tail = G.verxs[j]; Edge[length++].lowCost = G.arcs[i][j]; } } } // 将数组中的 所有元素 按 权值 从小到大排序 BubbleSort(length); // 辅助数组,表示各顶点自成一个连通分量 for (int i = 0; i < G.numVertexes; i++) { VexSet[i] = i; } // 依次查看数组 Edge 中的边 for (int i = 0; i < G.numEdges; i++) { int vHead = LocateVex(G, Edge[i].Head); // vHead 为边的始点 Head 的下标 int vTail = LocateVex(G, Edge[i].Tail); // vTail 为边的终点 Tail 的下标 int vsHead = VexSet[vHead]; // 获取边 Edge[i]的始点所在的连通分量 vsHead int vsTail = VexSet[vTail]; // 获取边 Edge[i]的终点所在的连通分量 vsTail if (vsHead != vsTail) { // 边的两个顶点分别属于不同的连通分量 if (i != 0) { printf(" -> "); } printf("(%c, %c)", Edge[i].Head, Edge[i].Tail); // 输出此边 cost += Edge[i].lowCost; for (int j = 0; j < G.numVertexes; j++) { // 合并 vsHead 和 vsTail两个分量,即两个集合统一编号 if (VexSet[j] == vsTail) { // 集合编号为 vsTail 的都改为 vsHead VexSet[j] = vsHead; } } } } } // 排序(小到大) void BubbleSort(int length) { int temp; char tHead; char tTail; for (int i = 0; i < length - 1; i++) { // 外层循环:轮次 int index = -1; for (int j = 0; j < length - 1 - i; j++) { // 内层循环:比较并交换位置(找出每轮最小数) if (Edge[j].lowCost > Edge[j + 1].lowCost) { temp = Edge[j + 1].lowCost; tHead = Edge[j + 1].Head; tTail = Edge[j + 1].Tail; Edge[j + 1].lowCost = Edge[j].lowCost; Edge[j + 1].Head = Edge[j].Head; Edge[j + 1].Tail = Edge[j].Tail; Edge[j].lowCost = temp; Edge[j].Head = tHead; Edge[j].Tail = tTail; index++; } } if (index == -1) { break; // 为提高排序效率,如果在每轮排序中未发生一次位置交换则代表已经是需要的顺序(直接跳出排序) } } } // 取顶点下标 int LocateVex(AMGraph G, char vex) { for (int i = 0; i < G.numVertexes; i++) { if (G.verxs[i] == vex) { return G.verxs[i] - 'A'; } } return FALSE; } // 输出代价 void PrintCost() { printf("%d ", cost); }

实验结果

C数据结构与算法——无向图(最小生成树) 应用_第1张图片

你可能感兴趣的:(C,c语言,算法,开发语言,学习,经验分享,图论)