聲明:圖片及內容基於https://www.bilibili.com/video/BV1yp4y1Q74o?from=articleDetail
最小生成樹原理
、
普利姆(Prim)算法
原理
Prim算法的實現
核心代碼
void MGraph::Prim(int start){ shortEdge shortEdge; //建立shortEdge數組
for(int i=0;i<vertexNum;i++){ shortEdge[i].lowcost=arc[start][i]; // 數組初始化,lowcost為鄰接矩陣第i行的權值
shortEdge[i].adjvex=start; //adjvex初始化為起始值
} shortEdge[start].lowcost=0;; //lowcost為0,把start放入集合
for(int i=0;i<vertexNum-1;i++){ int k=minEdge(shortEdge,vertexNum); //求出最小權值下標
outputSMT(k,shortEdge[k]); shortEdge[k].lowcost=0; //k放入集合
for(int j=0;j<vertexNum;j++){ //更新shortEdge數組,當前集合和剛進入集合的結點的權值比較
if(arc[k][j]<shortEdge[j].lowcost){ shortEdge[j].lowcost=arc[k][j]; shortEdge[j].adjvex=k; } } } }
int minEdge(shortEdge shortEdge,int vertexNum){ //求shortEdge數組中最小權值的下標
Edge e; //e是個臨時變量用來記錄當前小權值的下標和權值
e.lowcost=INFINIT; e.adjvex=-1; int i; for(i=0;i<vertexNum;i++){ //權值不為0(已經在集合中),不為無窮 (無路徑)
if(shortEdge[i].lowcost!=0&&shortEdge[i].lowcost!=INFINIT&&e.lowcost>shortEdge[i].lowcost){ e.lowcost=shortEdge[i].lowcost; e.adjvex=i; } } return e.adjvex; }
void outputSMT(int k,Edge Edge){ //打印路徑和權值
cout<<"("<<Edge.adjvex<<","<<k<<") "<<Edge.lowcost<<endl; }
完整代碼
#include<iostream>
#define MAXVEX 100
using namespace std; const int INFINIT=65535; int visit[MAXVEX]; typedef struct Edge{ int lowcost; int adjvex; }Edge,shortEdge[MAXVEX]; class MGraph { private: int *vertex; //頂點信息
int **arc; //鄰接矩陣
int vertexNum,arcNum; //頂點數,邊數
public: MGraph(int v[],int n,int e); ~MGraph(); void display(); void Prim(int start); }; MGraph::MGraph(int v[],int n,int e) { //n是頂點數,e是邊數
vertexNum=n; arcNum=e; vertex = new int[vertexNum]; //建立頂點信息
arc = new int*[vertexNum]; //建立鄰接表
for(int i=0; i<vertexNum; i++) arc[i]=new int[vertexNum]; for(int i=0; i<vertexNum; i++) { //初始化頂點信息
vertex[i]=v[i]; } for(int i=0;i<vertexNum;i++) //初始化鄰接表
for(int j=0;j<vertexNum;j++){ if(i==j) arc[i][j]=0; else arc[i][j]=INFINIT; } int vi,vj,w; for(int i=0;i<arcNum;i++){ cout<<"請輸入邊的兩個頂點和這條邊的權值"<<endl; cin>>vi>>vj>>w; //輸入邊依附的兩個頂點的編號 和權值
arc[vi][vj]=w; //有邊標志
arc[vj][vi]=w; } } void MGraph::display(){ for(int i=0;i<vertexNum;i++){ for(int j=0;j<vertexNum;j++){ if(arc[i][j]==INFINIT) cout<<"∞"<<"\t"; else cout<<arc[i][j]<<"\t"; } cout<<endl; } cout<<endl; for(int i=0;i<vertexNum;i++){ cout<<vertex[i]<<" "; } cout<<endl; } MGraph::~MGraph(){ delete []vertex; for(int i=0;i<vertexNum;i++) delete [] arc[i]; delete [] arc; } int minEdge(shortEdge shortEdge,int vertexNum){ //求shortEdge數組中最小權值的下標
Edge e; //e是個臨時變量用來記錄當前小權值的下標和權值
e.lowcost=INFINIT; e.adjvex=-1; int i; for(i=0;i<vertexNum;i++){ //權值不為0(已經在集合中),不為無窮 (無路徑)
if(shortEdge[i].lowcost!=0&&shortEdge[i].lowcost!=INFINIT&&e.lowcost>shortEdge[i].lowcost){ e.lowcost=shortEdge[i].lowcost; e.adjvex=i; } } return e.adjvex; } void outputSMT(int k,Edge Edge){ //打印路徑和權值
cout<<"("<<Edge.adjvex<<","<<k<<") "<<Edge.lowcost<<endl; } void MGraph::Prim(int start){ shortEdge shortEdge; //建立shortEdge數組
for(int i=0;i<vertexNum;i++){ shortEdge[i].lowcost=arc[start][i]; // 數組初始化,lowcost為鄰接矩陣第i行的權值
shortEdge[i].adjvex=start; //adjvex初始化為起始值
} shortEdge[start].lowcost=0;; //lowcost為0,把start放入集合
for(int i=0;i<vertexNum-1;i++){ //注意:i<vectexNum-1
int k=minEdge(shortEdge,vertexNum); //求出最小權值下標
outputSMT(k,shortEdge[k]); shortEdge[k].lowcost=0; //k放入集合
for(int j=0;j<vertexNum;j++){ //更新shortEdge數組,當前集合和剛進入集合的結點的權值比較
if(arc[k][j]<shortEdge[j].lowcost){ shortEdge[j].lowcost=arc[k][j]; shortEdge[j].adjvex=k; } } } } int main(){ int v[6]={0,1,2,3,4,5}; cout<<"請輸入頂點個數和邊的個數"<<endl; int m,n; cin>>m>>n; cout<<"請輸入prim算法的起點"<<endl; int k; cin>>k; MGraph mgraph(v,m,n); cout<<"輸出鄰接矩陣信息和邊數組信息:"<<endl; mgraph.display(); cout<<"輸出起點從"<<k<<"開始的最小生成樹:" <<endl; mgraph.Prim(k); return 0; }
輸入:
6 9
3
0 1 34
0 2 46
0 5 19
1 4 12
2 3 17
2 5 25
3 5 25
3 4 38
4 5 26
輸出:
輸出鄰接矩陣信息和邊數組信息:
0 34 46 ∞ ∞ 19
34 0 ∞ ∞ 12 ∞
46 ∞ 0 17 ∞ 25
∞ ∞ 17 0 38 25
∞ 12 ∞ 38 0 26
19 ∞ 25 25 26 0
0 1 2 3 4 5
輸出起點從3開始的最小生成樹:
(3,2) 17
(3,5) 25
(5,0) 19
(5,4) 26
(4,1) 12
克魯斯卡爾(Kruskal)算法
原理
Kruskal算法的實現
核心代碼
void EdgeGraph::Kruskal(){ for(int i=0;i<vertexNum;i++){ //parent數組初始化
parent[i]=-1; } sortEdge(); //邊集排序
int vex1,vex2,num=0; for(int i=0;i<edgeNum;i++){ vex1=findRoot(edge[i].from); //找到所在生成樹的根節點
vex2=findRoot(edge[i].to); //找到所在生成樹的根節點
if(vex1!=vex2){ //找到兩個根節點不相同,不會構成環
outputMST(edge[i]); //打印
parent[vex2]=vex1; //合並生成樹
num++; if(num==vertexNum-1) return; //循環vetexNum-1次,提前返回
} } }
int EdgeGraph::findRoot(int v){ //尋找根節點
int t=v; while(parent[t]>-1){ t=parent[t]; } return t; }
完整代碼
#include<iostream>
#define dataType int
using namespace std; const int MaxVertex=10; const int MaxEdge=100; struct EdgeType{ int from,to; int weight; }; class EdgeGraph{ private: dataType vertex[MaxVertex]; EdgeType edge[MaxEdge]; int vertexNum,edgeNum; int parent[MaxVertex]; public: EdgeGraph(int n,int e,dataType v[]); int findRoot(int v); void Kruskal(); void outputMST(EdgeType edge); void sortEdge(); }; EdgeGraph::EdgeGraph(int n,int e,dataType v[]){ vertexNum=n; //頂點數
edgeNum=e; //邊數
for(int i=0;i<vertexNum;i++){ vertex[i]=v[i]; } int start,end,w; for(int i=0;i<e;i++){ cout<<"請輸入第"<<i+1<<"條邊的兩個鄰接點和權值"<<endl; cin>>start>>end>>w; edge[i].from=start; edge[i].to=end; edge[i].weight=w; } } void EdgeGraph::Kruskal(){ for(int i=0;i<vertexNum;i++){ //parent數組初始化
parent[i]=-1; } sortEdge(); //邊集排序
int vex1,vex2,num=0; for(int i=0;i<edgeNum;i++){ vex1=findRoot(edge[i].from); //找到所在生成樹的根節點
vex2=findRoot(edge[i].to); //找到所在生成樹的根節點
if(vex1!=vex2){ //找到兩個根節點不相同,不會構成環
outputMST(edge[i]); //打印
parent[vex2]=vex1; //合並生成樹
num++; if(num==vertexNum-1) return; //循環vetexNum-1次,提前返回
} } } void EdgeGraph::sortEdge(){ bool flag=true; while(flag){ //優化版冒泡排序
flag=false; for(int i=0;i<edgeNum-1;i++){ for(int j=i+1;j<edgeNum;j++){ if(edge[i].weight>edge[j].weight){ flag=true; EdgeType t=edge[i]; edge[i]=edge[j]; edge[j]=t; } } } } } int EdgeGraph::findRoot(int v){ //尋找根節點
int t=v; while(parent[t]>-1){ t=parent[t]; } return t; } void EdgeGraph::outputMST(EdgeType edge){ cout<<"("<<edge.from<<","<<edge.to<<") "<<edge.weight<<endl; } int main(){ cout<<"請輸入結點數和邊數"<<endl; int n,e; cin>>n>>e; int v[MaxEdge]; cout<<"請輸入"<<n<<"個結點信息"<<endl; for(int i=0;i<n;i++) cin>>v[i]; EdgeGraph edgegraph(n,e,v); edgegraph.Kruskal(); return 0; }
輸入:
6 9
0 1 2 3 4 5
1 4 12
2 3 17
0 5 19
2 5 25
3 5 25
4 5 26
0 1 34
3 4 38
0 2 46
輸出:
(1,4) 12
(2,3) 17
(0,5) 19
(2,5) 25
(4,5) 26