程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 六講貫通C++圖的應用之四 最小生成樹(1)

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

編輯:C++入門知識

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

最小生成樹

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

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

最小生成樹的儲存

顯然用常用的樹的儲存方法來儲存沒有必要,雖然名曰“樹”,實際上,這裡誰是誰的“祖先”、“子孫”並不重要。因此,用如下的MSTedge結構數組來儲存就可以了。

  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條不產生回路最短的邊。Kruskal算法算是最直接的表達了這個思想——在剩余邊中挑選一條最短的邊,看是否產生回路,是放棄,不是選定然後重復這個步驟。說起來倒是很簡單,做起來就不那麼容易了——判斷是否產生回路需要並查集,在剩余邊中找一條最短的邊需要最小堆(並不需要對所有邊排序,所以堆是最佳選擇)。

Kruskal算法的復雜度是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. }  

下面是堆和並查集的實現

  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  


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved