龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > 软件开发 > C/C++开发 >

六讲贯通C++图的应用之四 最小生成树(1)

时间:2011-04-12 23:18来源:未知 作者:admin 点击:
分享到:
笔者从 基本储存方法 、 DFS和BFS 、 无向图 、 最小生成树 、 最短路径 以及 活动网络(AOV、AOE) 六个方面详细介绍 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  


精彩图集

赞助商链接