1. 克魯斯卡算法介紹
克魯斯卡爾(Kruskal)算法,是用來求加權連通圖的最小生成樹的算法。
基本思想:按照權值從小到大的順序選擇n-1條邊,並保證這n-1條邊不構成回路。
具體做法:首先構造一個只含n個頂點的森林,然后依權值從小到大從連通網中選擇邊加入到森林中,並使森林中不產生回路,直至森林變成一棵樹為止。
2. 克魯斯卡算法圖解
第1步:將邊<E,F>加入R中。
邊<E,F>的權值最小,因此將它加入到最小生成樹結果R中。
第2步:將邊<C,D>加入R中。
上一步操作之后,邊<C,D>的權值最小,因此將它加入到最小生成樹結果R中。
第3步:將邊<D,E>加入R中。
上一步操作之后,邊<D,E>的權值最小,因此將它加入到最小生成樹結果R中。
第4步:將邊<B,F>加入R中。
上一步操作之后,邊<C,E>的權值最小,但<C,E>會和已有的邊構成回路;因此,跳過邊<C,E>。同理,跳過邊<C,F>。將邊<B,F>加入到最小生成樹結果R中。
第5步:將邊<E,G>加入R中。
上一步操作之后,邊<E,G>的權值最小,因此將它加入到最小生成樹結果R中。
第6步:將邊<A,B>加入R中。
上一步操作之后,邊<F,G>的權值最小,但<F,G>會和已有的邊構成回路;因此,跳過邊<F,G>。同理,跳過邊<B,C>。將邊<A,B>加入到最小生成樹結果R中。
此時,最小生成樹構造完成!它包括的邊依次是:<E,F> <C,D> <D,E> <B,F> <E,G> <A,B>。
3. 代碼實現
(1)根據圖鏈表頂點信息得到所有邊信息
EData* listUDG::GetEdage() { EData *pEdata = new EData[m_nEdgNum]; ENode *pTemp = NULL; int nEdgNum = 0; for (int i = 0; i < m_nVexNum; i ++) { pTemp = m_mVexs[i].pFirstEdge; while(pTemp != NULL) { if (pTemp->nVindex > i) // 可以不需要改代碼,但是為了嚴謹和效率 { pEdata[nEdgNum].nStart = m_mVexs[i].data; pEdata[nEdgNum].nEnd = m_mVexs[pTemp->nVindex].data; pEdata[nEdgNum].nWeight = pTemp->nWeight; nEdgNum ++; } pTemp = pTemp->pNext; } } return pEdata; }
(2)對所有邊根據權重值大小進行排序
void listUDG::SortEdges(EData* edges, int elen) { int i,j; for (i=0; i<elen; i++) { for (j=i+1; j<elen; j++) { if (edges[i].nWeight > edges[j].nWeight) { // 交換"邊i"和"邊j" swap(edges[i], edges[j]); } } } }
(3)判斷選取的邊是否構成回路
若0->1->2->3->4 5->3
則a[0]=1;a[1]=2;a[2]=3;a[3]=4;
a[5]=3;
因為a[5]和a[2]的尾頂點都是3,則構成回路
int listUDG::GetEnd(int *vends, int i) { while(vends[i] != 0) { i = vends[i]; } return i; }
(4)Kruskal算法
// kruska最小生成樹 void listUDG::Kruskal() { // 得到所有的邊 EData *pEdata = GetEdage(); // 根據邊的權重進行排序 SortEdges(pEdata,m_nEdgNum); int vends[MAX] = {0}; // 用於保存"已有最小生成樹"中每個頂點在該最小樹中的終點。 EData rets[MAX]; // 結果數組,保存kruskal最小生成樹的邊 int nStartIndex,nEndIndex; int nIndex = 0; int m,n; for (int i = 0; i < m_nEdgNum; i ++) { nStartIndex = GetVIndex(pEdata[i].nStart); nEndIndex = GetVIndex(pEdata[i].nEnd); m = GetEnd(vends, nStartIndex); n = GetEnd(vends, nEndIndex); if (m != n) { vends[m] = n; // 設置m在"已有的最小生成樹"中的終點為n rets[nIndex++] = pEdata[i]; // 保存結果 } } delete[] pEdata; int nSum = 0; for (int i = 0; i < nIndex; i++) nSum += rets[i].nWeight; cout << "Kruskal=" << nSum << ": "; for (int i = 0; i < nIndex; i++) cout << "(" << rets[i].nStart << "," << rets[i].nEnd << ") "; cout << endl; }
(5)全部代碼

#include "stdio.h" #include <iostream> using namespace std; #define MAX 100 #define INF (~(0x1<<31)) // 最大值(即0X7FFFFFFF) class EData { public: EData(){} 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); ~listUDG(); void PrintUDG(); // Prim最小生成樹 void Prim(int nStart); // 得到所有的邊 EData* GetEdage(); // kruska最小生成樹 void Kruskal(); private: // 獲取<start, end>的權值,若start和end不是連接的,則返回無窮大 int GetWeight(int start, int end); // 返回頂點的索引 int GetVIndex(char ch); void LinkLast(ENode *pFirstNode, ENode *pNode); void QuickSort(EData* pEdata, int nEdgNum); void SortEdges(EData* edges, int elen); int GetEnd(int *vends, int i); private: int m_nVexNum; // 頂點數目 int m_nEdgNum; // 邊數目 VNode m_mVexs[MAX]; VNode m_PrimVexs[MAX]; }; listUDG::listUDG() { } 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::~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 listUDG::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 listUDG::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; } // 得到所有的邊 EData* listUDG::GetEdage() { EData *pEdata = new EData[m_nEdgNum]; ENode *pTemp = NULL; int nEdgNum = 0; for (int i = 0; i < m_nVexNum; i ++) { pTemp = m_mVexs[i].pFirstEdge; while(pTemp != NULL) { if (pTemp->nVindex > i) // 可以不需要改代碼,但是為了嚴謹和效率 { pEdata[nEdgNum].nStart = m_mVexs[i].data; pEdata[nEdgNum].nEnd = m_mVexs[pTemp->nVindex].data; pEdata[nEdgNum].nWeight = pTemp->nWeight; nEdgNum ++; } pTemp = pTemp->pNext; } } return pEdata; } // 根據權重值對所有的邊進行排序 void listUDG::SortEdges(EData* edges, int elen) { int i,j; for (i=0; i<elen; i++) { for (j=i+1; j<elen; j++) { if (edges[i].nWeight > edges[j].nWeight) { // 交換"邊i"和"邊j" swap(edges[i], edges[j]); } } } } int listUDG::GetEnd(int *vends, int i) { while(vends[i] != 0) { i = vends[i]; } return i; } // kruska最小生成樹 void listUDG::Kruskal() { // 得到所有的邊 EData *pEdata = GetEdage(); // 根據邊的權重進行排序 SortEdges(pEdata,m_nEdgNum); int vends[MAX] = {0}; // 用於保存"已有最小生成樹"中每個頂點在該最小樹中的終點。 EData rets[MAX]; // 結果數組,保存kruskal最小生成樹的邊 int nStartIndex,nEndIndex; int nIndex = 0; int m,n; for (int i = 0; i < m_nEdgNum; i ++) { nStartIndex = GetVIndex(pEdata[i].nStart); nEndIndex = GetVIndex(pEdata[i].nEnd); m = GetEnd(vends, nStartIndex); n = GetEnd(vends, nEndIndex); if (m != n) { vends[m] = n; // 設置m在"已有的最小生成樹"中的終點為n rets[nIndex++] = pEdata[i]; // 保存結果 } } delete[] pEdata; int nSum = 0; for (int i = 0; i < nIndex; i++) nSum += rets[i].nWeight; cout << "Kruskal=" << nSum << ": "; for (int i = 0; i < nIndex; i++) cout << "(" << rets[i].nStart << "," << rets[i].nEnd << ") "; cout << endl; } // 獲取<start, end>的權值,若start和end不是連接的,則返回無窮大 int listUDG::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 listUDG::GetVIndex(char ch) { int i = 0; for (; i < m_nVexNum; i ++) { if (m_mVexs[i].data == ch) { return i; } } return -1; } void listUDG::LinkLast(ENode *pFirstNode, ENode *pNode) { if (pFirstNode == NULL || pNode == NULL) { return; } ENode *pTempNode = pFirstNode; while (pTempNode->pNext != NULL) { pTempNode = pTempNode->pNext; } pTempNode->pNext = pNode; } 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); pG->Kruskal(); for (int i = 0; i < elen; i ++) { delete edges[i]; } return; }
注:感謝 skywang12345博主提供的內容分析,要了解更多詳細信息可參考原博 http://www.cnblogs.com/skywang12345/p/3711500.html