1. 程式人生 > >最小生成樹的兩種經典演算法--prim演算法和kruskal演算法

最小生成樹的兩種經典演算法--prim演算法和kruskal演算法

  • 一個連通圖的生成樹是圖的一個極小連通子圖,它包含所有頂點,但只有足以構成樹的n-1條邊
  • 這意味著對生成樹來說,砍去它的任何一條邊,就會使生成樹變成非連通圖,若給他增加一條邊就會形成一條迴路
  • 最小生成樹:權值最小的那顆生成樹叫~
  • 最小生成樹的性質:

    1. 最小生成樹並不唯一,準確的來說是最小生成樹的樹形並不唯一
    2. 最小生成樹的權值之和唯一,並且是最小的
    3. 最小生成樹的邊數=頂點數-1
  • 求最小生成樹有兩種經典演算法:普里姆演算法(prim)克魯斯卡爾(kruskal)演算法

普里姆演算法求最小生成樹程式碼(貼上即能跑)

#include <iostream>
#include<stdlib.h>
#define maxSize 100 #define infinity 65535 using namespace std; typedef struct{ char vnode[maxSize]; int edge[maxSize][maxSize]; int n,e; }MGraph; void createMGraph(MGraph &G){//鄰接矩陣構造圖 int i,j,n,e,k,w; cout<< "請輸入圖的頂點數和邊數"<<endl; cin>> G.n >> G.e; n=G.n; e=G.e; cout
<< "請輸入頂點"<<endl; for(i=0;i<n;i++){ cin>> G.vnode[i]; } for(i=0;i<n;i++){ for(j=0;j<n;j++){ G.edge[i][j]=infinity; } } cout<< "請輸入邊的下標i,j"<<endl; for(k=0;k<e;k++){ scanf("%d %d %d",&i,&j,&w); G.edge[i][j]=w; G.edge[j][i]=G.edge[i][j]; } } void
minSpanTree_prim(MGraph G){//prim演算法求最小生成樹 int n,e,i,j,min,k,t; n=G.n; int lowcost[maxSize];//為0表示頂點加入最小生成樹,其他存放邊的權值 int adjvex[maxSize];//這個存放頂點下標,標明頂點是新增頂點,還是之前遍歷過的頂點 lowcost[0]=0;//把首個頂點(下標為0的頂點)加入到最小生成樹 adjvex[0]=0;//下標為0 for(i=1;i<n;i++){//迴圈0下標頂點與其他頂點的連線情況 (不從0開始是因為0 0表示自己和自己的環) lowcost[i]=G.edge[0][i];//把0下標頂點和其他頂點組成的邊的權值存放到lowcost陣列中 adjvex[i]=0;//當前lowcost陣列中的邊的權值的起始頂點全部都是下標為0的頂點,而結束頂點則是序號為lowcost陣列下標的頂點 } cout<<"最小生成樹為:"<<endl; for(t=1;t<n;t++){//迴圈所有頂點,構造最小生成樹 min=infinity;//初始化min,剛開始為一個極大值 j=1;k=0; while(j<n){//遍歷lowcost陣列 if(lowcost[j]!=0&&lowcost[j]<min){//除去已加入最小生成樹的頂點 min=lowcost[j];//找出lowcost陣列中最小的權值,並賦值給min k=j;//記錄最小權值的下標 (這個k其實就是權值最小的那條邊的結束頂點) } j++; } printf("(%d,%d)\n",adjvex[k],k);//列印權值最小的那條邊的起始頂點和結束頂點 lowcost[k]=0;//把k下標的頂點加入到最小生成樹 for(i=1;i<n;i++){//遍歷頂點 if(lowcost[i]!=0&&G.edge[k][i]<lowcost[i]){//要除去已加入最小生成樹的頂點 lowcost[i]=G.edge[k][i];//在k結點與其他頂點鄰接的權值和lowcost陣列中取較小的一方更新lowcost adjvex[i]=k;//記錄較小權值的邊的起始頂點下標 } } } } int main(int argc, char** argv) { MGraph G; createMGraph(G); minSpanTree_prim(G); return 0; } /* 示例輸入: 頂點數和邊數: 9 15 輸入頂點: 0 1 2 3 4 5 6 7 8 輸入頂點下標和權值: 4 7 7 2 8 8 0 1 10 0 5 11 1 8 12 3 7 16 1 6 16 5 6 17 1 2 18 6 7 19 3 4 20 3 8 21 2 3 22 3 6 24 4 5 26 */

克魯斯卡爾演算法構造最小生成樹程式碼:

#include <iostream>
#include<algorithm>
#include<stdlib.h>
#define maxSize 100
#define infinity 65535
using namespace std;
typedef struct{//鄰接矩陣構造的圖結點 
    char vnode[maxSize];
    int edge[maxSize][maxSize];
    int n,e;
}MGraph; 

typedef struct{//邊集結點(存放邊的頂點下標和邊的權重) 
    int start;//邊起點 
    int end;//邊終點 
    int w;//邊權值 
}Road; 
Road road[maxSize];//邊集陣列
int parent[maxSize];
int getRoot(int i){//此函式用於找到下標為i的頂點在生成樹中的父節點 (並查集) 
    while(parent[i]!=i){  
        i=parent[i];
    }
    return i;
}

bool compare(Road x,Road y){//自定義結構體比較方式,按結構體中的權值升序排 
    return x.w<y.w;
}

void createMGraph(MGraph &G){//建立圖 
    int i,j,n,e,k,w;
    cout<< "請輸入圖的頂點數和邊數"<<endl;
    cin>> G.n >> G.e;
    n=G.n;
    e=G.e;
    cout<< "請輸入頂點"<<endl;
    for(i=0;i<n;i++){
        cin>> G.vnode[i]; 
    }
    for(i=0;i<n;i++){
        for(j=0;j<n;j++){
            G.edge[i][j]=infinity;
        }
    }
    cout<< "請輸入邊的下標i,j"<<endl;
    for(k=0;k<e;k++){
        scanf("%d %d %d",&i,&j,&w);
        G.edge[i][j]=w;
        road[k].start=i;//建立的時候就給邊集陣列賦值
        road[k].end=j;
        road[k].w=w;
        G.edge[j][i]=G.edge[i][j];//根據無向圖鄰接矩陣的對稱性賦值
    } 
}

int kruskal(MGraph G){//克魯斯卡爾演算法 
    int a,b,sum=0;
    int e=G.e;
    for(int i=0;i<G.e;i++){//初始化根結點下標陣列 
        parent[i]=i;//為存放根節點下標陣列賦初值,自身作為自己的根節點 
    } 
    sort(road,road+e,compare);//按邊集陣列中的權值由小到大排序 
    for(int i=0;i<e;i++){//遍歷已經排好序的邊 
        a=getRoot(road[i].start);//獲取開始頂點在生成樹中的父節點
        b=getRoot(road[i].end);//獲取終結頂點在生成樹中的父節點
        if(a!=b){//當ab不等說明兩者不是同一個父節點,不會構成環 
            parent[a]=b;//把b點作為孩子加在a的後面 
            printf("(%d,%d)\n",road[i].start,road[i].end);//列印構成最小生成樹的邊
            sum+=road[i].w;//最小生成樹的權值總和
        }
    }
    printf("sum=%d\n",sum);
    return sum;
}
int main(int argc, char** argv) {
    MGraph G;
    createMGraph(G);
    kruskal(G);
    return 0;
}
/*
    示例輸入:
        頂點數和邊數: 9 15
        輸入頂點:   0 1 2 3 4 5 6 7 8
        輸入頂點下標和權值: 
                4 7 7
                2 8 8
                0 1 10
                0 5 11
                1 8 12
                3 7 16
                1 6 16
                5 6 17
                1 2 18
                6 7 19
                3 4 20
                3 8 21
                2 3 22
                3 6 24
                4 5 26
    */

相關推薦

小生成樹演算法的區別以及Prim演算法與Dijkstra演算法的區別

Prim和Kruskal的不同之處在於兩者選擇的變數不同,Prim選擇的是始終保持權值最小,然後逐個加點構建一棵樹。而Kruskal則是始終保證是一棵樹(雖然構建過程中不一定是真正的樹,但並查集判環可以這樣理解:是為了保證結果是一顆樹),然後逐條加邊,使權值最小。 知道上述

小生成樹演算法比較與實現

Kruskal演算法 :(並查集) 時間複雜度O(elog2e),適合簡單圖。 演算法步驟: 1.構造一個有n個頂點的無邊子圖; 2.從原圖選擇邊權最小的邊加入該子圖,直至子圖成為一棵樹; 3.邊能加入子圖的條件是,邊的兩個端點u,v還未連

小生成樹圖文詳解(Prim演算法

最小生成樹 就像幾個村莊都不相通, 要修路, 怎麼修, 這個花的錢最少, 這種最優選擇就是最小生成樹 設G = (V, E)是無向連通圖(V是結點集, E是邊集),相對於村莊例子,V就是那些村莊的集合,E就是村莊之間路的集合

圖的小生成樹(普利姆prim演算法

什麼是生成樹呢? 一個連通圖的生成樹是指一個極小連通子圖, 它含有圖中的全部頂點,但只有足以構成一棵樹的n-1條邊。 什麼是最小生成樹? 在一個連通圖的所有生成樹中,各邊的代價之和最小的那棵生成樹稱為該連通圖的最小代價生成樹(MST), 簡稱最小生成樹。 求最小生成樹有兩種演算法,

python機器學習案例系列教程——小生成樹(MST)的Prim演算法Kruskal演算法

最小生成樹MST 一個有 n 個結點的連通圖的生成樹是原圖的極小連通子圖,且包含原圖中的所有 n 個結點,並且有保持圖連通的最少的邊。 也就是說,用原圖中有的邊,連線n個節點,保證每個節點都被連線,且使用的邊的數目最少。 最小權重生成樹 在一給定

hiho一下 第二十八週 小生成樹三·堆優化的Prim演算法

描述 回到兩個星期之前,在成功的使用Kruscal演算法解決了問題之後,小Ho產生了一個疑問,究竟這樣的演算法在稀疏圖上比Prim優化之處在哪裡呢? 提示:沒有無緣無故的優化! 輸入 每個測試點(輸入檔案)有且僅有一組測試資料。 在一組測試資料中: 第1行為2個整數N、M,表示小Hi擁有的城市數量和小H

#1109 : 小生成樹三·堆優化的Prim演算法

時間限制:10000ms 單點時限:1000ms 記憶體限制:256MB 描述 回到兩個星期之前,在成功的使用Kruscal演算法解決了問題之後,小Ho產生了一個疑問,究竟這樣的演算法在稀疏圖上比Prim優化之處在哪裡呢? 提示:沒有無緣無故的優化! 輸入 每個測

hiho 29 小生成樹三·堆優化的Prim演算法

問題描述 最小生成樹演算法,在稀疏圖時,Kruscal複雜度更低,我們可以使用堆優化的prim演算法達到與Kruscal一樣的複雜度。 Prim演算法本身的時間複雜度是O(N^2)的,而在這個演算法中,使用了堆來維護所有的邊,運算元一共是O(M)級別的,所以

JS實現小生成樹之克魯斯卡爾(Kruskal演算法

  克魯斯卡爾演算法列印最小生成樹:   構造出所有邊的集合 edges,從小到大,依次選出篩選邊列印,遇到閉環(形成迴路)時跳過。 JS程式碼: 1 //定義鄰接矩陣 2 let Arr2 = [ 3 [0, 10, 65535, 65535, 65535,

小生成樹之克魯斯卡爾(Kruskal演算法

附轉載連結:https://www.cnblogs.com/yoke/p/6697013.html學習最小生成樹演算法之前我們先來了解下 下面這些概念:樹(Tree):如果一個無向連通圖中不存在迴路,則這種圖稱為樹。生成樹 (Spanning Tree):無向連通圖G的一個子

小生成樹經典演算法--prim演算法kruskal演算法

一個連通圖的生成樹是圖的一個極小連通子圖,它包含所有頂點,但只有足以構成樹的n-1條邊 這意味著對生成樹來說,砍去它的任何一條邊,就會使生成樹變成非連通圖,若給他增加一條邊就會形成一條迴路 最小生成樹:權值最小的那顆生成樹叫~ 最小生成樹的性質: 最小生成樹

小生成樹方法(Kruskal演算法Prim演算法

關於圖的幾個概念定義: 連通圖:在無向圖中,若任意兩個頂點vivi與vjvj都有路徑相通,則稱該無向圖為連通圖。 強連通圖:在有向圖中,若任意兩個頂點vivi與vjvj都有路徑相通,則稱該有向圖為強連通圖。 連通網:在連通圖中,若圖的邊具有一定的意義,每一條邊都對應著一個數,稱

ac之小生成樹經典演算法

傳送門佈線問題時間限制:1000 ms  |  記憶體限制:65535 KB難度:4描述南陽理工學院要進行用電線路改造,現在校長要求設計師設計出一種佈線方式,該佈線方式需要滿足以下條件:1、把所有的樓都供上電。2、所用電線花費最少輸入第一行是一個整數n表示有n組測試資料。(n

小生成樹演算法PrimKruskal演算法

越來越明白了一個道理:你寫不出程式碼的原因只有一個,那就是你沒有徹底理解這個演算法的思想!! 以前寫過最小生成樹,但是,水了幾道題後,過了一段時間,就會忘卻,一點也寫不出來了。也許原因只有一個,那就是我沒有徹底理解這兩種演算法。 主題: 其實,求最小生成樹有兩個要點,一個是

【硬核遊戲攻略】1. 小生成樹演算法及《我的世界》中迷宮的一鍵生成函式

  這個系列的第一篇,雖然起名叫硬核攻略… 但我想開篇還是寫點簡單的,諸如Prim,Kruskal之類的MST生成演算法已經爛大街了,這裡重新實現一遍Prim,然後基於生成的迷宮自動建立一系列對應的mcfunction,用於在遊戲中一鍵呼叫.這個系列不出意外的

小生成樹基本的演算法

prim /** @Cain*/ #include <bits/stdc++.h> using namespace std; const int maxn=2005; const int inf=1e9+5; int edge[maxn][ma

[Sicily 1090 Highways] 求小生成樹演算法(普里姆演算法/克魯斯卡爾演算法

(1)問題描述: 政府建公路把所有城市聯絡起來,使得公路最長的邊最短,輸出這個最長的邊。 (2)基本思路: 使得公路最長的邊最短其實就是要求最小生成樹。 (3)程式碼實現: 普里姆演算法: #

圖的小生成樹演算法之C++封裝

最小生成樹定義:     給定一無向帶權圖,頂點數是n,要使圖連通只需n-1條邊,若這n-1條邊的權值和最小,則稱有這n個頂點和n-1條邊構成了圖的最小生成樹(minimum-cost spanning tree)MST。 兩種最小生成樹演算法: 一、prim演算法

圖的小生成樹prim演算法詳解

prim演算法是求圖的最小生成樹的一種演算法,它是根據圖中的節點來進行求解,具體思想大概如下: 首先,將圖的所有節點(我們假定總共有n個節點)分成兩個集合,V和U。其中,集合V儲存的是我們已經訪問過的節點,集合U儲存的是我們未曾訪問的節點。prim演算法第一步就是選定第一個節點放入集合

poj 2485 小生成樹 Prim演算法 模板

嗯,沒錯是純模板,只要會模板就能ac的, 在這裡還是講一下模板的意思吧:大致就是現在起始點附近搜距離他最近的點v1,然後再以v1為點去搜距離v1最近的點,且之前都過的點不能再搜了,不明白的就去手畫一下過程吧。 程式碼: #include<cstdio> #include&