c/c++ 用克魯斯卡爾(kruskal)算法構造最小生成樹
阿新 • • 發佈:2018-08-05
方向 查看 維數 dbo 一個 code lse size 只需要
c/c++ 用克魯斯卡爾(kruskal)算法構造最小生成樹
最小生成樹(Minimum Cost Spanning Tree)的概念:
假設要在n個城市之間建立公路,則連通n個城市只需要n-1條線路。這時,自然會考慮,如何在最節省經費的前提下建立這個公路網絡。
每2個城市之間都可以設置一條公路,相應地都要付出一定的經濟代價。n個城市之間,最多可以設置n(n-1)/2條線路,那麽,如何在這些可能的線路中選擇n-1條,以使總的耗費最少?
克魯斯卡爾(kruskal)算法的大致思路:
把每條邊的權重按照從小到大排序後,連接。連接時,需要查看要連接的兩個頂點的父節點是否相同,不同才可以連接,連接後,更新父節點。
圖為下圖:
第一步
圖1
第二步
圖2
第三步
圖3
第四步
A->D,C->D,B->C的權重都是5,這時就不知道連哪個了,所以要創建2個輔助函數is_same,mark_same。
is_same用來判斷要連接的2個點的父節點是否相同,如果相同就說明了,連接後,圖就存在了環,所以不可以連接,放棄這條邊,去尋找下一條邊。
mark_same用來更新節點的父節點。
當拿到的節點是AD時,發現AD的父節點都是A,所以放棄;
當拿到的節點是CD時,發現AD的父節點都是A,所以放棄;
當拿到的節點是BC時,發現B的父節點是自己,C的父節點是A,父節點不同,所以連接,並更父節點
圖4
找一半的矩陣,把各條邊的起點,終點,權重,放到edge數組裏
圖5
mixSpanTree.h
#ifndef __mixspantree__ #define __mixspantree__ #include <stdio.h> #include <malloc.h> #include <assert.h> #include <memory.h> #include <stdlib.h> #include <stdbool.h> #define Default_vertex_size 20 #define T char//dai biao ding dian de lei xing #define E int #define MAX_COST 0x7FFFFFFF typedef struct GraphMtx{ int MaxVertices;//zui da ding dian shu liang] int NumVertices;//shi ji ding dian shu liang int NumEdges;//bian de shu lian T* VerticesList;//ding dian list int** Edge;//bian de lian jie xin xi, bu shi 0 jiu shi 1 }GraphMtx; typedef struct Edge{ int begin;//邊的起點 int end; //邊的終點 E cost; //邊的權重 }Edge; //chu shi hua tu void init_graph(GraphMtx* gm); //打印二維數組 void show_graph(GraphMtx* gm); //插入頂點 void insert_vertex(GraphMtx* gm, T v); //添加頂點間的線 void insert_edge(GraphMtx* gm, T v1, T v2, E cost); //用kruskal算法構造最小生成樹 void minSpanTree_kruskal(GraphMtx* gm); #endif
mixSpanTree.c
#include "mixSpanTree.h"
void init_graph(GraphMtx* gm){
gm->MaxVertices = Default_vertex_size;
gm->NumEdges = gm->NumVertices = 0;
//kai pi ding dian de nei cun kong jian
gm->VerticesList = (T*)malloc(sizeof(T) * (gm->MaxVertices));
assert(NULL != gm->VerticesList);
//創建二維數組
//讓一個int的二級指針,指向一個有8個int一級指針的數組
//開辟一個能存放gm->MaxVertices個int一級指針的內存空間
gm->Edge = (int**)malloc(sizeof(int*) * (gm->MaxVertices));
assert(NULL != gm->Edge);
//開辟gm->MaxVertices組,能存放gm->MaxVertices個int的內存空間
for(int i = 0; i < gm->MaxVertices; ++i){
gm->Edge[i] = (int*)malloc(sizeof(int) * gm->MaxVertices);
}
//初始化二維數組
//讓每個頂點之間的邊的關系都為不相連的
for(int i = 0; i < gm->MaxVertices; ++i){
for(int j = 0; j < gm->MaxVertices; ++j){
if(i == j)
gm->Edge[i][j] = 0;
else
gm->Edge[i][j] = MAX_COST;
}
}
}
//打印二維數組
void show_graph(GraphMtx* gm){
printf(" ");
for(int i = 0; i < gm->NumVertices; ++i){
printf("%c ", gm->VerticesList[i]);
}
printf("\n");
for(int i = 0; i < gm->NumVertices; ++i){
//在行首,打印出頂點的名字
printf("%c:", gm->VerticesList[i]);
for(int j = 0; j < gm->NumVertices; ++j){
if(gm->Edge[i][j] == MAX_COST){
printf("%c ", ‘*‘);
}
else{
printf("%d ", gm->Edge[i][j]);
}
}
printf("\n");
}
printf("\n");
}
//插入頂點
void insert_vertex(GraphMtx* gm, T v){
//頂點空間已滿,不能再插入頂點了
if(gm->NumVertices >= gm->MaxVertices){
return;
}
gm->VerticesList[gm->NumVertices++] = v;
}
int getVertexIndex(GraphMtx* gm, T v){
for(int i = 0; i < gm->NumVertices; ++i){
if(gm->VerticesList[i] == v)return i;
}
return -1;
}
//添加頂點間的線
void insert_edge(GraphMtx* gm, T v1, T v2, E cost){
if(v1 == v2)return;
//查找2個頂點的下標
int j = getVertexIndex(gm, v1);
int k = getVertexIndex(gm, v2);
//說明找到頂點了,並且點之間還沒有線
if(j != -1 && k != -1 ){
//因為是無方向,所以更新2個值
gm->Edge[j][k] = gm->Edge[k][j] = cost;
//邊數加一
gm->NumEdges++;
}
}
//比較邊的權重,本函數作為快速排序函數的參數來使用。
int cmp(const void* a, const void* b){
return ((*(Edge*)a).cost - (*(Edge*)b).cost);
}
//判斷參數的2個頂點的父節點是否相同
bool is_same(int* father, int begin, int end){
while(father[begin] != begin){
begin = father[begin];
}
while(father[end] != end){
end = father[end];
}
return begin == end;
}
//找到end節點的父節點x,再找到begin節點的父節點y,更新x節點的父節點為y
void mark_same(int* father, int begin, int end){
while(father[begin] != begin){
begin = father[begin];
}
while(father[end] != end){
end = father[end];
}
father[end] = begin;
}
//用kruskal算法構造最小生成樹
void minSpanTree_kruskal(GraphMtx* g){
int n = g->NumVertices;
Edge* edge = (Edge*)malloc(sizeof(Edge) * n*(n-1)/2);
assert(edge != NULL);
int k = 0;
//查找一半的矩陣,把各條邊的起點,終點,權重,放到edge數組裏,參照上面的圖5
for(int i = 0; i < n; ++i){
for(int j = i; j < n; j++){
if(g->Edge[i][j] != 0 && g->Edge[i][j] != MAX_COST){
edge[k].begin = i;
edge[k].end = j;
edge[k].cost = g->Edge[i][j];
k++;
}
}
}
//按照權重來排序(用系統函數)
//第一個參數:要被排序的數組
//第二個參數:數組中元素的個數
//第三個參數:每個數組元素占用的內存空間
//第四個參數:函數指針,指定排序的規則
qsort(edge, k, sizeof(Edge), cmp);
//初始化每個節點的父節點,讓每個節點的父節點為自身
int *father = (int*)malloc(sizeof(int) * n);
assert(NULL != father);
for(int i = 0; i < n; ++i){
father[i] = i;
}
for(int i = 0; i < n; ++i){
//判斷2個節點的父節點是否相同,不相同就連接
if(!is_same(father, edge[i].begin, edge[i].end)){
printf("%c->%c:%d\n",g->VerticesList[edge[i].begin],g->VerticesList[edge[i].end], edge[i].cost);
//連接後,找到節點end的父節點x,再找到節點begin的父節點y,把節點x的父節點更新為y
mark_same(father, edge[i].begin, edge[i].end);
}
}
}
mixSpanTreemain.c
#include "mixSpanTree.h"
int main(){
GraphMtx gm;
//初始化圖
init_graph(&gm);
//插入頂點
insert_vertex(&gm, ‘A‘);
insert_vertex(&gm, ‘B‘);
insert_vertex(&gm, ‘C‘);
insert_vertex(&gm, ‘D‘);
insert_vertex(&gm, ‘E‘);
insert_vertex(&gm, ‘F‘);
//添加連線
insert_edge(&gm, ‘A‘, ‘B‘, 6);
insert_edge(&gm, ‘A‘, ‘D‘, 5);
insert_edge(&gm, ‘A‘, ‘C‘, 1);
insert_edge(&gm, ‘B‘, ‘E‘, 3);
insert_edge(&gm, ‘B‘, ‘C‘, 5);
insert_edge(&gm, ‘C‘, ‘E‘, 6);
insert_edge(&gm, ‘C‘, ‘D‘, 5);
insert_edge(&gm, ‘C‘, ‘F‘, 4);
insert_edge(&gm, ‘F‘, ‘E‘, 6);
insert_edge(&gm, ‘D‘, ‘F‘, 2);
//打印圖
show_graph(&gm);
//kruskal
minSpanTree_kruskal(&gm);
}
完整代碼
編譯方法:gcc -g mixSpanTree.c mixSpanTreemain.c
c/c++ 用克魯斯卡爾(kruskal)算法構造最小生成樹