最小生成树的Kruskal算法
最小生成树是连接图中所有顶点代价最小的树,通用算法是对于最小生成树的一个顶点子集A,设全体顶点集合为V,则跨越集合{A,V-A}中最小的的边为安全边,可以加入到最小生成树中。
Kruskal算法采用了不相交的集合森林,把每一个顶点初始化为一个单元素的集合,我再定义了一个边的结构,用于连接两个不同的集合。先用最小堆,以权重非递减的顺序处理边,若边的两个顶点不在同一个集合中,则将他们合并到同一个集合中,如此循环,知道所有的集合变为1个集合为止。
// disjoint_set_forest.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include<iostream> #include<time.h> #include<queue> using namespace std; typedef int ElementType; #define numOfVertex 10 //图中顶点的个数 typedef int vertex; //不相交的集合森林////////////////////////////////// typedef struct TreeNode *Node; struct TreeNode { ElementType key; int rank; Node parent; }; //生成只有一个元素的新集合///////////////////////////////////////// Node makeSet(ElementType x) { Node node = (Node)malloc(sizeof(TreeNode)); node->key = x; node->rank = 0; node->parent = node; return node; } //这里传递的参数不能是ElementType类型,因为一开始所有的点都被初始化为一个集合,所以用node类型 Node findSet(Node node) { if (node != node->parent) node->parent = findSet(node->parent); return node->parent; } //连接两个集合的函数////////////////////////////////////////////////// Node Link(Node node1, Node node2) { if (node1->rank > node2->rank) { node2->parent = node1; return node1; } else { node1->parent = node2; if (node1->rank == node2->rank) node2->rank++; return node2; } } Node Union(Node x, Node y) { return Link(findSet(x), findSet(y)); } /////////////////////////////////////////////////////////////////////////// //边的结构,重定义了操作符////////////////////////////////////////////// struct Edge { Node node1, node2; //边的两个顶点 int weight; //边的权重 friend bool operator< (Edge edge1, Edge edge2) //重定义运算符 { return edge1.weight > edge2.weight; //因为优先队列默认是<,因此若要形成最小堆,用>来重定义< } }; void Mst_Kruskal(int graph[numOfVertex][numOfVertex]) { //先把每一个顶点生成一个单独的集合,以0为起点 Node node[numOfVertex]; for (int i = 0; i < numOfVertex; i++) node[i] = makeSet(i); //初始化边的结构 Edge edgeTemp; priority_queue<Edge> edgess; //最小堆 for (int i = 0; i < numOfVertex; i++) for (int j = i + 1; j < numOfVertex; j++) { if (graph[i][j] != 0) { edgeTemp.weight = graph[i][j]; edgeTemp.node1 = node[i]; edgeTemp.node2 = node[j]; edgess.push(edgeTemp); } } //用队列来存放选中的边 queue<Edge> result; while (!edgess.empty()) { if (findSet(edgess.top().node1) != findSet(edgess.top().node2)) { result.push(edgess.top()); Union((edgess.top()).node1, (edgess.top()).node2); } edgess.pop(); } //打印选中的边 while (!result.empty()) { edgeTemp = result.front(); result.pop(); cout << edgeTemp.node1->key << "----" << edgeTemp.node2->key << endl;; } } int main() { //用初始化一个邻接矩阵的权重,注意只用到了上三角矩阵 srand((int)time(0)); //产生随机数种子 int graph[numOfVertex][numOfVertex]; for(int i=0;i<numOfVertex;i++) for (int j = i+1; j < numOfVertex; j++) graph[i][j]= rand() % 20; //打印初始化后的邻接矩阵 for (int i = 0; i < numOfVertex; i++) { for (int k = 0; k < i + 1; k++) cout << ' ' << '\t'; for (int j = i + 1; j < numOfVertex; j++) { cout << graph[i][j] << '\t'; } cout << endl; } //调用最小生成树的算法 Mst_Kruskal(graph); while (1); return 0; }