自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

六講貫通C++圖的應(yīng)用之四 最小生成樹

開發(fā) 后端
圖的應(yīng)用恐怕是C++所有數(shù)據(jù)結(jié)構(gòu)中最寬泛的了,本次六講筆者從基本儲存方法、DFS和BFS、無向圖、最小生成樹、最短路徑以及活動網(wǎng)絡(luò)(AOV、AOE)六個方面詳細介紹圖的應(yīng)用。本文是這次系列文章的第四篇,主要介紹最小生成樹。

  筆者從基本儲存方法、DFS和BFS無向圖、最小生成樹、最短路徑以及活動網(wǎng)絡(luò)(AOV、AOE)六個方面詳細介紹C++圖的應(yīng)用。這篇我們該介紹最小生成了。

  最小生成樹

  說人是最難伺候的,真是一點不假。上面剛剛為了“提高可靠性”添加了幾條多余的邊,這會兒又來想辦法怎么能以最小的代價把所有的頂點都連起來??赡苷侨说倪@種精神才使得人類能夠進步吧——看著現(xiàn)在3GHz的CPU真是眼紅啊,我還在受500MHz的煎熬,然后再想想8086……

  正如圖的基本元素是頂點和邊,從這兩個方向出發(fā),就能得到兩個算法——Kruskal算法(從邊出發(fā))、Prim算法(從頂點出發(fā))。據(jù)說還有別的方法,恕我參考資料有限,不能詳查。

  最小生成樹的儲存

  顯然用常用的樹的儲存方法來儲存沒有必要,雖然名曰“樹”,實際上,這里誰是誰的“祖先”、“子孫”并不重要。因此,用如下的MSTedge結(jié)構(gòu)數(shù)組來儲存就可以了。

  1. template <class dist>   
  2. class MSTedge   
  3. {   
  4. public:   
  5. MSTedge() {}   
  6. MSTedge(int v1, int v2, dist cost) : v1(v1), v2(v2), cost(cost) {}   
  7. int v1, v2;   
  8. dist cost;   
  9. bool operator > (const MSTedge& v2) { return (cost > v2.cost); }   
  10. bool operator < (const MSTedge& v2) { return (cost < v2.cost); }   
  11. bool operator == (const MSTedge& v2) { return (cost == v2.cost); }   
  12. };  

  Kruskal算法

  最小生成樹直白的講就是,挑選N-1條不產(chǎn)生回路最短的邊。Kruskal算法算是最直接的表達了這個思想——在剩余邊中挑選一條最短的邊,看是否產(chǎn)生回路,是放棄,不是選定然后重復(fù)這個步驟。說起來倒是很簡單,做起來就不那么容易了——判斷是否產(chǎn)生回路需要并查集,在剩余邊中找一條最短的邊需要最小堆(并不需要對所有邊排序,所以堆是最佳選擇)。

  Kruskal算法的復(fù)雜度是O(eloge),當e接近N^2時,可以看到這個算法不如O(N^2)的Prim算法,因此,他適合于稀疏圖。而作為稀疏圖,通常用鄰接表來儲存比較好。另外,對于鄰接矩陣儲存的圖,Kruskal算法比Prim算法占不到什么便宜(初始還要掃描N^2條“邊”)。因此,最好把Kruskal算法放在Link類里面。

  1. template <class name, class dist> int Link::MinSpanTree(MSTedge* a)   
  2. {   
  3. MinHeap > E; int i, j, k, l = 0;   
  4. MFSets V(vNum); list::iterator iter;   
  5. for (i = 0; i < vNum; i++)   
  6. for (iter = vertices[i].e->begin(); iter != vertices[i].e->end(); iter++)   
  7. E.insert(MSTedge(i, iter->vID, iter->cost));//建立邊的堆   
  8. for (i = 0; i < eNum && l < vNum; i++)//Kruskal Start   
  9. {   
  10. j = V.find(E.top().v1); k = V.find(E.top().v2);   
  11. if (j != k) { V.merge(j, k); a[l] = E.top(); l++; }   
  12. E.pop();   
  13. }   
  14. return l;   
  15. }  

  下面是堆和并查集的實現(xiàn)

  1. #ifndef Heap_H   
  2. #define Heap_H   
  3. #include    
  4. using namespace std;   
  5. #define minchild(i) (heap[i*2+1] 
  6. template <class T>   
  7. class MinHeap   
  8. {   
  9. public:   
  10. void insert(const T& x) { heap.push_back(x); FilterUp(heap.size()-1); }   
  11. const T& top() { return heap[0]; }   
  12. void pop() { heap[0] = heap.back(); heap.pop_back(); FilterDown(0); }   
  13. private:   
  14. void FilterUp(int i)   
  15. {   
  16. for (int j = (i - 1) / 2; j >= 0 && heap[j] > heap[i]; i = j, j = (i - 1) / 2)   
  17. swap(heap[i], heap[j]);   
  18. }   
  19. void FilterDown(int i)   
  20. {   
  21. for (int j = minchild(i); j < heap.size() && heap[j] < heap[i]; i = j, j = minchild(i))   
  22. swap(heap[i], heap[j]);   
  23. }   
  24. vector heap;   
  25. };   
  26. #endif   
  27.  
  28. #ifndef MFSets_H   
  29. #define MFSets_H   
  30. class MFSets   
  31. {   
  32. public:   
  33. MFSets(int maxsize) : size(maxsize)   
  34. {   
  35. parent = new int[size + 1];   
  36. for (int i = 0; i <= size; i++) parent[i] = -1;   
  37. }   
  38. ~MFSets() { delete []parent; }   
  39. void merge(int root1, int root2)//root1!=root2   
  40. {   
  41. parent[root2] = root1;   
  42. }   
  43. int find(int n)   
  44. {   
  45. if (parent[n] < 0) return n;   
  46. return find(parent[n]);   
  47. }   
  48. private:   
  49. int size;   
  50. int* parent;   
  51. };   
  52. #endif  

#p#

  Prim算法

  如果從頂點入手,就能得到另一種方法。從只含有一個頂點的集合開始,尋找集合外面的頂點到這個集合里的頂點最近的一條邊,然后將這個頂點加入集合,修改因為這個頂點的加入而使得集合外面的頂點到集合里的頂點的最短距離產(chǎn)生變化的分量。因為需要對每個頂點掃描,鄰接矩陣儲存的圖是最合適Prim算法的。

  1. template <class name, class dist> int AdjMatrix::MinSpanTree(MSTedge* a)   
  2. {   
  3. dist* lowC = new dist[vNum]; int* nearV = new int[vNum];   
  4. int i, j, k;   
  5. for (i = 0; i < vNum; i++) { lowC[i] = edge[0][i]; nearV[i] = 0; } nearV[0] = -1;   
  6. for (k = 0; k < vNum-1; k++)//Prim Start   
  7. {   
  8. for (i = 1, j = 0; i < vNum; i++)   
  9. if (nearV[i] != -1 && lowC[i] < lowC[j]) j = i;//find low cost   
  10. a[k] = MSTedge(nearV[j], j, lowC[j]); nearV[j] = -1; //insert MST   
  11. if (a[k].cost == NoEdge) return k - 1;//no edge then return   
  12. for (i = 1; i < vNum; i++)//modify low cost   
  13. if (nearV[i] != -1 && edge[i][j] < lowC[i]) { lowC[i] = edge[i][j]; nearV[i] = j; }   
  14. }   
  15. return k;   
  16. }  

  【附注】這里需要說明一下,對于edge[I][I]這樣的是應(yīng)該是0呢還是NoEdge呢?顯然0合理,但是不好用。并且,從有權(quán)圖無權(quán)圖統(tǒng)一的角度來說,是NoEdge更好。因此,在我的有權(quán)圖的鄰接矩陣中,主對角線上的元素是NoEdge,而不是書上的0。

  測試程序

  儲存和操作分離,沒想到得到了一個有趣的結(jié)果——對于最后的無向圖而言,最小生成樹的算法對外表現(xiàn)不知道是采用了那個算法。

  1. template <class name, class dist, class mem>   
  2. bool Graph::MinSpanTree()   
  3. {   
  4. MSTedge* a = new MSTedge[vNum() - 1];   
  5. int n = data.MinSpanTree(a); dist sum = dist();   
  6. if (n < vNum() - 1) return false;//不夠N-1條邊,不是生成樹   
  7. for (int i = 0; i < n; i++)   
  8. {   
  9. cout << '(' << getV(a[i].v1) << ',' << getV(a[i].v2) << ')' << a[i].cost << ' ';   
  10. sum += a[i].cost;   
  11. }   
  12. cout << endl << "MinCost: " << sum << endl;   
  13. delete []a;   
  14. return true;   
  15. }  

  最后的測試圖的數(shù)據(jù)取自《數(shù)據(jù)結(jié)構(gòu)算法與應(yīng)用-C++語言描述》(中文譯名)

  1. #include    
  2. using namespace std;   
  3. #include "Graph.h"   
  4. int main()   
  5. {   
  6. Graph<charint, AdjMatrix<charint> > a(100);//改為Link儲存為Kruskal算法   
  7. a.insertV('A'); a.insertV('B');   
  8. a.insertV('C'); a.insertV('D');   
  9. a.insertV('E'); a.insertV('F');   
  10. a.insertV('G');   
  11. a.insertE('A''B', 28); a.insertE('A''F', 10);   
  12. a.insertE('B''C', 16); a.insertE('C''D', 12);   
  13. a.insertE('D''E', 22); a.insertE('B''G', 14);   
  14. a.insertE('E''F', 25); a.insertE('D''G', 18);   
  15. a.insertE('E''G', 24);   
  16. a.MinSpanTree();   
  17. return 0;   
  18. }  

【編輯推薦】

  1. 經(jīng)典四講貫通C++排序之一 插入排序
  2. c++編程常用工具
  3. 給C++初學(xué)者的50個忠告
  4. c++最基礎(chǔ)的20條規(guī)則
  5. 程序員必看 c++筆試題匯總
責任編輯:韓亞珊 來源: 天極網(wǎng)
相關(guān)推薦

2011-04-11 16:10:55

無向圖C++

2011-04-11 16:43:51

AOVAOE活動網(wǎng)絡(luò)

2011-04-11 16:32:28

路徑C++

2011-04-11 15:57:22

DFSBFSC++

2011-04-11 15:53:40

C++

2011-04-11 14:52:18

選擇排序排序C++

2023-11-27 15:01:21

Prim算法Kruskal算法

2011-04-11 14:21:43

希爾排序排序C++

2011-04-11 13:41:34

插入排序排序C++

2011-04-11 14:29:44

交換排序冒泡排序排序

2019-09-09 14:33:17

開發(fā)者技能算法

2021-09-29 18:28:41

數(shù)據(jù)結(jié)構(gòu)算法最小生成樹

2010-07-06 15:46:41

UDP協(xié)議

2010-01-14 09:27:44

C++語言

2010-01-20 11:02:42

C++開發(fā)環(huán)境

2011-03-30 17:20:18

C++引用

2024-01-23 12:54:00

C++編程語言代碼

2021-02-20 06:13:18

C 語言C++

2010-02-03 15:27:26

C++ static

2010-02-03 09:52:52

C++指針與引用
點贊
收藏

51CTO技術(shù)棧公眾號