普里姆算法介紹
普里姆(Prim)算法,是用來求加權連通圖的最小生成樹算法
基本思想:對於圖G而言,V是所有頂點的集合;現在,設置兩個新的集合U和T,其中U用於存放G的最小生成樹中的頂點,T存放G的最小生成樹中的邊。 從所有uЄU,vЄ(V-U) (V-U表示出去U的所有頂點)的邊中選取權值最小的邊(u, v),將頂點v加入集合U中,將邊(u, v)加入集合T中,如此不斷重復,直到U=V為止,最小生成樹構造完畢,這時集合T中包含了最小生成樹中的所有邊。
代碼實現
1. 思想邏輯
(1)以無向圖的某個頂點(A)出發,計算所有點到該點的權重值,若無連接取最大權重值#define INF (~(0x1<<31))
(2)找到與該頂點最小權重值的頂點(B),再以B為頂點計算所有點到改點的權重值,依次更新之前的權重值,注意權重值為0或小於當前權重值的不更新,因為1是一當找到最小權重值的頂點時,將權重值設為了0,2是會出現無連接的情況。
(3)將上述過程一次循環,並得到最小生成樹。
2. Prim算法
// Prim最小生成樹 void Prim(int nStart) { int i = 0; int nIndex=0; // prim最小樹的索引,即prims數組的索引 char cPrims[MAX]; // prim最小樹的結果數組 int weights[MAX]; // 頂點間邊的權值 cPrims[nIndex++] = m_mVexs[nStart].data; // 初始化"頂點的權值數組", // 將每個頂點的權值初始化為"第start個頂點"到"該頂點"的權值。 for (i = 0; i < m_nVexNum; i++) { weights[i] = GetWeight(nStart, i); } for (i = 0; i < m_nVexNum; i ++) { if (nStart == i) { continue; } int min = INF; int nMinWeightIndex = 0; for (int k = 0; k < m_nVexNum; k ++) { if (weights[k]!= 0 && weights[k] < min) { min = weights[k]; nMinWeightIndex = k; } } // 找到下一個最小權重值索引 cPrims[nIndex++] = m_mVexs[nMinWeightIndex].data; // 以找到的頂點更新其他點到該點的權重值 weights[nMinWeightIndex]=0; int nNewWeight = 0; for (int ii = 0; ii < m_nVexNum; ii++) { nNewWeight = GetWeight(nMinWeightIndex, ii); // 該位置需要特別注意 if (0 != weights[ii] && weights[ii] > nNewWeight) { weights[ii] = nNewWeight; } } } // 計算最小生成樹的權重值 int nSum = 0; for (i = 1; i < nIndex; i ++) { int min = INF; int nVexsIndex = GetVIndex(cPrims[i]); for (int kk = 0; kk < i; kk ++) { int nNextVexsIndex = GetVIndex(cPrims[kk]); int nWeight = GetWeight(nVexsIndex, nNextVexsIndex); if (nWeight < min) { min = nWeight; } } nSum += min; } // 打印最小生成樹 cout << "PRIM(" << m_mVexs[nStart].data <<")=" << nSum << ": "; for (i = 0; i < nIndex; i++) cout << cPrims[i] << " "; cout << endl; }
3. 全部實現

#include "stdio.h" #include <iostream> using namespace std; #define MAX 100 #define INF (~(0x1<<31)) // 最大值(即0X7FFFFFFF) class EData { public: EData(char start, char end, int weight) : nStart(start), nEnd(end), nWeight(weight){} char nStart; char nEnd; int nWeight; }; // 邊 struct ENode { int nVindex; // 該邊所指的頂點的位置 int nWeight; // 邊的權重 ENode *pNext; // 指向下一個邊的指針 }; struct VNode { char data; // 頂點信息 ENode *pFirstEdge; // 指向第一條依附該頂點的邊 }; // 無向鄰接表 class listUDG { public: listUDG(){}; listUDG(char *vexs, int vlen, EData **pEData, int elen) { m_nVexNum = vlen; m_nEdgNum = elen; // 初始化"鄰接表"的頂點 for (int i = 0; i < vlen; i ++) { m_mVexs[i].data = vexs[i]; m_mVexs[i].pFirstEdge = NULL; } char c1,c2; int p1,p2; ENode *node1, *node2; // 初始化"鄰接表"的邊 for (int j = 0; j < elen; j ++) { // 讀取邊的起始頂點和結束頂點 c1 = pEData[j]->nStart; c2 = pEData[j]->nEnd; p1 = GetVIndex(c1); p2 = GetVIndex(c2); node1 = new ENode(); node1->nVindex = p2; node1->nWeight = pEData[j]->nWeight; if (m_mVexs[p1].pFirstEdge == NULL) { m_mVexs[p1].pFirstEdge = node1; } else { LinkLast(m_mVexs[p1].pFirstEdge, node1); } node2 = new ENode(); node2->nVindex = p1; node2->nWeight = pEData[j]->nWeight; if (m_mVexs[p2].pFirstEdge == NULL) { m_mVexs[p2].pFirstEdge = node2; } else { LinkLast(m_mVexs[p2].pFirstEdge, node2); } } } ~listUDG() { ENode *pENode = NULL; ENode *pTemp = NULL; for (int i = 0; i < m_nVexNum; i ++) { pENode = m_mVexs[i].pFirstEdge; if (pENode != NULL) { pTemp = pENode; pENode = pENode->pNext; delete pTemp; } delete pENode; } } void PrintUDG() { ENode *pTempNode = NULL; cout << "鄰接無向表:" << endl; for (int i = 0; i < m_nVexNum; i ++) { cout << "頂點:" << GetVIndex(m_mVexs[i].data)<< "-" << m_mVexs[i].data<< "->"; pTempNode = m_mVexs[i].pFirstEdge; while (pTempNode) { cout <<pTempNode->nVindex << "->"; pTempNode = pTempNode->pNext; } cout << endl; } } // Prim最小生成樹 void Prim(int nStart) { int i = 0; int nIndex=0; // prim最小樹的索引,即prims數組的索引 char cPrims[MAX]; // prim最小樹的結果數組 int weights[MAX]; // 頂點間邊的權值 cPrims[nIndex++] = m_mVexs[nStart].data; // 初始化"頂點的權值數組", // 將每個頂點的權值初始化為"第start個頂點"到"該頂點"的權值。 for (i = 0; i < m_nVexNum; i++) { weights[i] = GetWeight(nStart, i); } for (i = 0; i < m_nVexNum; i ++) { if (nStart == i) { continue; } int min = INF; int nMinWeightIndex = 0; for (int k = 0; k < m_nVexNum; k ++) { if (weights[k]!= 0 && weights[k] < min) { min = weights[k]; nMinWeightIndex = k; } } // 找到下一個最小權重值索引 cPrims[nIndex++] = m_mVexs[nMinWeightIndex].data; // 以找到的頂點更新其他點到該點的權重值 weights[nMinWeightIndex]=0; int nNewWeight = 0; for (int ii = 0; ii < m_nVexNum; ii++) { nNewWeight = GetWeight(nMinWeightIndex, ii); // 該位置需要特別注意 if (0 != weights[ii] && weights[ii] > nNewWeight) { weights[ii] = nNewWeight; } } } // 計算最小生成樹的權重值 int nSum = 0; for (i = 1; i < nIndex; i ++) { int min = INF; int nVexsIndex = GetVIndex(cPrims[i]); for (int kk = 0; kk < i; kk ++) { int nNextVexsIndex = GetVIndex(cPrims[kk]); int nWeight = GetWeight(nVexsIndex, nNextVexsIndex); if (nWeight < min) { min = nWeight; } } nSum += min; } // 打印最小生成樹 cout << "PRIM(" << m_mVexs[nStart].data <<")=" << nSum << ": "; for (i = 0; i < nIndex; i++) cout << cPrims[i] << " "; cout << endl; } private: // 獲取<start, end>的權值,若start和end不是連接的,則返回無窮大 int GetWeight(int start, int end) { if (start == end) { return 0; } ENode *pTempNode = m_mVexs[start].pFirstEdge; while (pTempNode) { if (end == pTempNode->nVindex) { return pTempNode->nWeight; } pTempNode = pTempNode->pNext; } return INF; } // 返回頂點的索引 int GetVIndex(char ch) { int i = 0; for (; i < m_nVexNum; i ++) { if (m_mVexs[i].data == ch) { return i; } } return -1; } void LinkLast(ENode *pFirstNode, ENode *pNode) { if (pFirstNode == NULL || pNode == NULL) { return; } ENode *pTempNode = pFirstNode; while (pTempNode->pNext != NULL) { pTempNode = pTempNode->pNext; } pTempNode->pNext = pNode; } private: int m_nVexNum; // 頂點數目 int m_nEdgNum; // 邊數目 VNode m_mVexs[MAX]; VNode m_PrimVexs[MAX]; }; void main() { char vexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'}; // 邊 EData *edges[] = { // 起點 終點 權 new EData('A', 'B', 12), new EData('A', 'F', 16), new EData('A', 'G', 14), new EData('B', 'C', 10), new EData('B', 'F', 7), new EData('C', 'D', 3), new EData('C', 'E', 5), new EData('C', 'F', 6), new EData('D', 'E', 4), new EData('E', 'F', 2), new EData('E', 'G', 8), new EData('F', 'G', 9) }; int vlen = sizeof(vexs)/sizeof(vexs[0]); int elen = sizeof(edges)/sizeof(edges[0]); listUDG* pG = new listUDG(vexs, vlen, edges, elen); pG->PrintUDG(); // 打印圖 pG->Prim(0); for (int i = 0; i < elen; i ++) { delete edges[i]; } return; }