這是中科大2020年春《算法分析與設計》的課程實驗3。
實驗題目
助教給出補充,所給樹的節點一定是20,也就是說默認輸入輸出都是一個20*20的矩陣。
我已經在GitHub上傳我的代碼和測試樣例,這是鏈接。
https://github.com/Xixo99/exp3_Prim-Kruskal
具體算法原理我就不解釋了,因爲我覺得我表達能力肯定沒有那些寫書的人好哈哈。
代碼
/*******************
* 描述:最小生成樹
* 分別用Prim算法和Kruskal算法實現
* 輸入是一個20*20的矩陣,以txt的格式讀入
* 輸出是一個20*20的矩陣,以txt的格式輸出
*時間:2020/5/11
*******************/
#include<stdio.h>
#include<stdlib.h>
const int Size = 20; //節點的個數,這裏給定了是20
const int INF = 2147483647; //此爲int可表示整數範圍內的最大正數
int G[Size][Size],Ans[Size][Size],B[Size][Size]; //G爲全連接圖中的邊權重,對角線均爲0,Ans矩陣爲答案矩陣,B矩陣在Kruskal算法中有應用
//Node 和 count 的含義在兩種算法中不同,請注意區分
int Node[Size];
int count;
//用於在Prim算法中
//判斷函數節點是否已經加入“被連接的節點”
bool NotInNode(int j) {
for (int i=0;i <= count;i++){
if (j == Node[i]) {
return false;
}
}
return true;
}
void Prim(){
//在本算法中,Node數組用於記錄已被加入連接的節點
int start,end;
int node;
for (int i=1; i< Size ; i++) {
Node[i] = -1;
}
Node[0] = 0 ; //第一個引入的節點是0
int min = INF;
for(count = 0; count < Size - 1 ; count++) { //總共要新增Size-1條邊
for (int i = 0;i <= count; i++) { //找出count+1個已經在樹裏的節點,遍歷他們的所有連向其他節點的邊
node = Node[i];
for(int j =0; j < Size ; j++) { //遍歷一個節點的所有邊
if (j != node && NotInNode(j)) {
if (G[node][j] < min) {
min = G[node][j];
start = node;
end = j;
}
}
}
}
//將最小值的結果保存在Ans數組中
Ans[start][end] = 1;
Ans[end][start] = 1;
min = INF;
Node[count+1] = end;
start = -1;
end = -1;
}
return ;
}
bool visit(int i) {
Node[i] = 0; //表示節點未訪問完成
for (int j = 0;j<Size;j++) {
if (B[i][j] == 1) {
if (Node[j] == 0) return true; //訪問一個未訪問完成的節點說明存在迴路
B[i][j] = 0;
B[j][i] = 0;
if (visit(j)) return true;
}
}
Node[i] = 1; //表示節點已經訪問完畢
return false;
}
bool IsRoop() { //判斷圖中是否存在環
//初始Node[i]均爲-1
for (int i=0;i<Size;i++) {
Node[i] = -1;
}
for (int i=0;i<Size-1;i++) {
if(Node[i] == -1) {
if (visit(i)) return true; //訪問i時發現環路
}
}
return false; //未發現環路
}
void Kruskal(){
int last_min,start,end;
for (count = 0; count < Size -1; count++){ //增加Size-1條邊
int min = INF; //初始化最小值
for(int i = 0; i < Size-1;i++) {
for (int j = i + 1; j < Size; j++) {
if (G[i][j] < min && G[i][j] >= last_min) {
for (int t1=0;t1 < Size;t1++) {
for (int t2=0;t2 < Size;t2++) {
B[t1][t2] = Ans[t1][t2];
}
}
B[i][j] = 1;
B[j][i] = 1;
if(!IsRoop()) { //沒有環路就更新最小值
min = G[i][j];
start = i;
end = j;
}
}
}
}
//更新Ans數組,保存答案
Ans[start][end]=1;
Ans[end][start]=1;
last_min = min;
G[start][end] = INF;
G[end][start] = INF;
}
}
int main(void){
FILE *pr = NULL,*pw = NULL;
//讀入test.txt,寫在output.txt
pr = fopen("test.txt", "r");
if (pr == NULL ) {
printf("Can't open 'test.txt'!\n");
return 1;
}
pw = fopen("output.txt","w");
if (pw == NULL) {
printf("Can't open 'output.txt'!\n");
return 2;
}
//更新權重,順便將Ans數組初始化
for(int i = 0; i < Size; i++) {
for (int j = 0; j < Size; j++) {
Ans[i][j] = 0;
fscanf(pr,"%d",&G[i][j]);
}
}
//兩種方案均可選,臨時註釋掉不需要用的另外一種就可以了
Prim();
// Kruskal();
//輸出數組
for(int i = 0; i < Size; i++) {
for(int j = 0 ; j <= i; j++ ) {
fprintf(pw,"%d\t",Ans[i][j]);
printf("%d ",Ans[i][j]);
}
fprintf(pw,"\n");
printf("\n");
}
return 0;
}
感謝大家的支持和點贊。