最小生成樹的兩種經典演算法--prim演算法和kruskal演算法
- 一個連通圖的
生成樹
是圖的一個極小連通子圖,它包含所有頂點,但只有足以構成樹的n-1
條邊 - 這意味著對生成樹來說,砍去它的任何一條邊,就會使生成樹變成非連通圖,若給他增加一條邊就會形成一條迴路
最小生成樹
:權值最小的那顆生成樹叫~最小生成樹的性質:
- 最小生成樹並不唯一,準確的來說是
最小生成樹的樹形並不唯一
- 最小生成樹的權值之和唯一,並且是最小的
- 最小生成樹的邊數=頂點數-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
最小生成樹的兩種演算法:Prim和Kruskal演算法
越來越明白了一個道理:你寫不出程式碼的原因只有一個,那就是你沒有徹底理解這個演算法的思想!! 以前寫過最小生成樹,但是,水了幾道題後,過了一段時間,就會忘卻,一點也寫不出來了。也許原因只有一個,那就是我沒有徹底理解這兩種演算法。 主題: 其實,求最小生成樹有兩個要點,一個是
【硬核遊戲攻略】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&