關於圖的最小生成路徑——Kruskal演算法和Prime演算法

我的可樂要加冰!發表於2020-10-27

關於圖的最小生成路徑——Kruskal演算法和Prime演算法

寫部落格其實就是為了回顧所學的~現在學起這些東西都更輕鬆了…


/**
	Kruskal演算法:關鍵三步——1、排序;2、判斷迴圈條件;3、找符合要求的邊;
 	關鍵:用到了並查集;
 **/


#include<iostream>
using namespace std ;
const int Max_Size = 100 ;
const int  INFINITY = 65535 ;
int n,e;

typedef struct ENode{
    int v1,v2 ;
    int w ;
}ENode;
ENode E[Max_Size];

int Father[Max_Size];

void Create(){//建圖
    cin >> n >> e ;
    for(int i = 0 ;i<e;i++){
        cin >> E[i].v1 >> E[i].v2 >> E[i].w;
    }
}
bool cmp(ENode &a,ENode &b){
    return a.w < b.w;
}

int Find_Father(int x ){//並查集(其實就是找到各個節點的根節點,然後壓縮路徑)
    int a = x ;
    while (x != Father[x]){//回溯找到根節點
        x = Father[x] ;
    }
    while (a != Father[a]){//壓縮路徑
        int z = a ;
        a = Father[a];
        Father[z] = x ;
    }
    return x ;
}

int sum = 0 ;
int Kruskal(){
    int num = 0 ;//迴圈次數(跳出迴圈的條件二:邊數 = 節點數-1——樹的性質)
    for(int i = 0 ;i<n ; i++ ){//初始化,將各節點的根節點置為自身
        Father[i] = i ;
    }
    
    sort(E,E+e,cmp);
    
    for(int i = 0 ;i<e ;i++){//對邊操作,所以小於邊數
        int fa_v1 = Find_Father(E[i].v1);
        int fa_v2 = Find_Father(E[i].v2);
        
        if(fa_v1 != fa_v2){//在不同的連通塊(根節點不一樣)
            Father[fa_v1] = fa_v2;  //加入並查集
            sum += E[i].w;  //權值累加
            num ++ ;
            if(num == n -1){
                break ;
            }
        }
        
    }
    if(num != n-1){//不等於,則代表圖不連通,此時樹肯定也不連通
        return -1 ;
    }
    
    return sum ;
}

int main (){
    Create();
    cout<<"最小路徑為:" << Kruskal() << endl ;
    
    return 0 ;
}

下面是關於Prime演算法(個人覺得可以跟Dijkstra一起學)


/**
 prime演算法與Dijkstra差不多,唯一不同的就是
 prime演算法是最小路徑,計算過程是集合與新的節點來比較距離;
 而Dijkstra是點與點來比較距離;
 
 **/


#include<iostream>
using namespace std ;
const int Max_Size = 100 ;
const int  INFINITY = 65535 ;
int n,e;

int Visited[Max_Size];
int Dis[Max_Size];          //新節點與集合的距離
int G[Max_Size][Max_Size];

void Create(){
    cin >> n >> e ;
    for(int i = 0 ;i<n ;i++ ){//初始化圖
        for(int j = 0 ;j< n ;j++ ){
            if(i == j){
                G[i][j] = 0 ;
            }else {
                G[i][j] = INFINITY;
            }
        }
    }
    
    for(int i = 0;i<e;i++){
        int v1,v2,w;//邊的兩個端點,權值
        cin >> v1 >> v2 >> w ;
        G[v1][v2] = w ;
        G[v2][v1] = w ;
    }
}
int sum = 0 ;
int Prime(){
    fill(Dis, Dis+Max_Size, INFINITY);//用fill不用memset的原因,後者賦值大數會出問題
    memset(Visited, 0, sizeof(Visited));
    Dis[0] = 0 ;//規定起始節點(無所謂哪個)
    
    for(int i =0 ;i< n ;i++){//對節點操作,所以是小於n
        
        int u = -1 ,min = INFINITY ;
        for(int j = 0 ;j< n;j++ ){//同Dijkstra,找最短
            if(Visited[j] == 0 && Dis[j] < min ){
                u = j ;
                min = Dis[j];
            }
        }
        if(u == -1 )    {
            sum = -1 ;
            return -1;
        }
        Visited[u] = 1 ;
        sum += Dis[u];//相當於將u加入到集合了
        
        for(int j = 0 ;j< n ;j++ ){
            if(Visited[j] == 0  && G[u][j] < Dis[j]){//這裡判斷條件,跟Dijkstra也不一樣,這兒是點到集合的距離
                Dis[j] = G[u][j];
            }
        }
        
    }
    
    return sum ;
}

int main (){
    Create();
    cout<<"最小路徑為:" << Prime() << endl ;
    
    return 0 ;
}

如有問題,歡迎大家指正~

相關文章