資料結構:最小生成樹--Prim演算法
阿新 • • 發佈:2019-02-09
最小生成樹:Prim演算法
最小生成樹
給定一無向帶權圖,頂點數是n,要使圖連通只需n-1條邊,若這n-1條邊的權值和最小,則稱有這n個頂點和n-1條邊構成了圖的最小生成樹(minimum-cost spanning tree)。
Prim演算法
Prim演算法是解決最小生成樹的常用演算法。它採取貪心策略,從指定的頂點開始尋找最小權值的鄰接點。圖G=<V,E>,初始時S={V0},把與V0相鄰接,且邊的權值最小的頂點加入到S。不斷地把S中的頂點與V-S中頂點的最小權值邊加入,直到所有頂點都已加入到S中。
演算法說明
為了方便尋找最小權值的邊,構建一最近邊 結構體CloseEdge:
//最近邊
typedef struct closeedge_tag
{
int adjvex; //鄰接點
int weight; //權值
}CloseEdge;
建立一陣列CloseEdge closeedge[n];頂點u屬於S,頂點v屬於V-S,則closeedge[v].weight=min{weight(u,v)};closeedge[v].adjvex=u;另外設定一bool型的陣列add,標記頂點i是否已加入S。結合closeedge和add即可得到當前最小權值邊。每當有新的節點加入S時,則需更新closeedge。具體細節看程式碼。例項
從V0開始
程式碼
類定義
#include<iostream> #include<iomanip> #include<stack> using namespace std; #define MAXWEIGHT 100 //邊 typedef struct edge_tag { int tail; int head; }Edge; //最近邊 typedef struct closeedge_tag { int adjvex; //鄰接點 int weight; //權值 }CloseEdge; class Graph { private: //頂點數 int numV; //邊數 int numE; //鄰接矩陣 int **matrix; public: Graph(int numV); //建圖 void createGraph(int numE); //析構方法 ~Graph(); //Prim演算法 void Prim(int); int minEdgeVex(CloseEdge*, bool*); void updateCloseEdge(CloseEdge*, bool*, int); //列印鄰接矩陣 void printAdjacentMatrix(); //檢查輸入 bool check(int, int, int); };
類實現
//建構函式,指定頂點數目
Graph::Graph(int numV)
{
//對輸入的頂點數進行檢測
while (numV <= 0)
{
cout << "頂點數有誤!重新輸入 ";
cin >> numV;
}
this->numV = numV;
//構建鄰接矩陣,並初始化
matrix = new int*[numV];
int i, j;
for (i = 0; i < numV; i++)
matrix[i] = new int[numV];
for (i = 0; i < numV; i++)
for (j = 0; j < numV; j++)
{
if (i == j)
matrix[i][i] = 0;
else
matrix[i][j] = MAXWEIGHT;
}
}
void Graph::createGraph(int numE)
{
/*
對輸入的邊數做檢測
一個numV個頂點的有向圖,最多有numV*(numV - 1)條邊
*/
while (numE < 0 || numE > numV*(numV - 1))
{
cout << "邊數有問題!重新輸入 ";
cin >> numE;
}
this->numE = numE;
int tail, head, weight, i;
i = 0;
cout << "輸入每條邊的起點(弧尾)、終點(弧頭)和權值" << endl;
while (i < numE)
{
cin >> tail >> head >> weight;
while (!check(tail, head, weight))
{
cout << "輸入的邊不正確!請重新輸入 " << endl;
cin >> tail >> head >> weight;
}
//Prim演算法主要針對的是無向圖
matrix[tail][head] = weight;
matrix[head][tail] = weight;
i++;
}
}
Graph::~Graph()
{
int i;
for (i = 0; i < numV; i++)
delete[] matrix[i];
delete[]matrix;
}
/*
Prim演算法
求最小生成樹
*/
void Graph::Prim(int vertex)
{
//有numV個頂點的圖的最小生成樹有numV-1條邊
Edge *edges = new Edge[numV - 1];
//標記頂點是否加入
bool *add = new bool[numV];
memset(add, 0, numV);
//先把vertex加入
add[vertex] = true;
//最近邊
CloseEdge *closeedge = new CloseEdge[numV];
int i;
//初始化最近邊
for (i = 0; i < numV; i++)
{
closeedge[i].weight = matrix[vertex][i];
if (!add[i] && matrix[vertex][i] > 0 && matrix[vertex][i] < MAXWEIGHT)
closeedge[i].adjvex = vertex;
}
int v, count = 0;
while (count < numV - 1)
{
//獲取最近邊的鄰接點
v = minEdgeVex(closeedge, add);
add[v] = true;
//把最小權值邊依次加入陣列edges
edges[count].tail = closeedge[v].adjvex;
edges[count].head = v;
//更新最近邊
updateCloseEdge(closeedge, add, v);
count++;
}
cout << "從頂點 " << vertex << " 開始,最小生成樹的邊是" << endl;
for (i = 0; i < count; i++)
cout << edges[i].tail << "---" << edges[i].head << endl;
//釋放空間
delete[]edges;
delete[]add;
delete[]closeedge;
}
//從closeedge中尋找最小邊的鄰接頂點
int Graph::minEdgeVex(CloseEdge *closeedge, bool *add)
{
int i, v, w;
v = 0;
w = MAXWEIGHT;
for (i = 0; i < numV ; i++)
if (!add[i] && closeedge[i].weight < w)
{
w = closeedge[i].weight;
v = i;
}
return v;
}
//頂點v的加入後,需要更新最近邊
void Graph::updateCloseEdge(CloseEdge* closeedge, bool *add, int v)
{
int i;
for (i = 0; i < numV; i++)
if (!add[i] && matrix[v][i] < closeedge[i].weight)
{
closeedge[i].adjvex = v;
closeedge[i].weight = matrix[v][i];
}
}
//列印鄰接矩陣
void Graph::printAdjacentMatrix()
{
int i, j;
cout.setf(ios::left);
cout << setw(7) << " ";
for (i = 0; i < numV; i++)
cout << setw(7) << i;
cout << endl;
for (i = 0; i < numV; i++)
{
cout << setw(7) << i;
for (j = 0; j < numV; j++)
cout << setw(7) << matrix[i][j];
cout << endl;
}
}
bool Graph::check(int tail, int head, int weight)
{
if ((tail == head) || tail < 0 || tail >= numV
|| head < 0 || head >= numV
|| weight <= 0 || weight >= MAXWEIGHT)
return false;
return true;
}
主函式
int main()
{
cout << "******Prim***by David***" << endl;
int numV, numE;
cout << "建圖..." << endl;
cout << "輸入頂點數 ";
cin >> numV;
Graph graph(numV);
cout << "輸入邊數 ";
cin >> numE;
graph.createGraph(numE);
cout << endl << "Prim..." << endl;
/*
由於輸出結果太長,不利於截圖,故只打印一半的節點
要想獲得從所有節點開始的最小生成樹,修改i的變化範圍即可
*/
for (int i = 0; i < numV / 2; i++)
graph.Prim(i);
system("pause");
return 0;
}
執行
若有所幫助,頂一個哦!
專欄目錄: