Prim算法学习总结及实现
Prim算法的核心思想就是在上一步确定的两个互斥点集A,B中找出一对可以形成边的点,并且这一对点构成的边应该是两个点集中可构成的边里权值最小的。
然后把这一条边加入边集E,并且把终点加入到A集合,从B集合中去掉。再重复,直到B集为空或者A集满。
具体来讲
1、设立点集A,只包括一个点,设立点集B,包括图中剩余的其他点
2、从点集A,B各选一点a,b 满足a-b边是所有点集A与点集B的点构成的边的权值最小的。把a-b边加入边集,把b从点集B中去掉,添加到点集A。
重复2,直到B点集为空或A点集满。
3、由边集即可构成一个最小生成树。
至于代码,实现方式不少,主要差异在如何体现AB点集。
我的数据结构书上是利用该点的一个标志位(是否被访问过)来标定属于A,B点集的。
下面放上我的代码,因为基本上是照书上打的,所以基本没有注释。
// Prim.cpp : 定义控制台应用程序的入口点。 // 作者:王锦 // 邮箱:jinksw@vip.qq.com #include "stdafx.h" #include <iostream> using namespace std; class Edge{//定义边类 public: int from,to,weight; Edge(){ from = -1; to = -1; weight = 0; } Edge(int from,int to,int weight){ this->from = from; this->to = to; this->weight = weight; } }; class EdgeInfo{//定义邻接表边界点内数据类 public: int vertex; int weight; }; template<class T> class Node{//定义结点类 public: T element; Node *next; Node(const T&element, Node *next = NULL){ this->element = element; this->next = next; } Node(Node *next = NULL){ this->next = next; } }; template<class T> class HeadNode{//定义头结点类 public: Node<T> *head; HeadNode(){ head = new Node<T>(); } }; const int UNVISITED = -1;//定义未访问标记常量 const int VISITED = 1;//定义已访问标记常量 const int INFINITY = 100000;//定义无穷大常量 class Graph{//图类定义 public: int vertexNum; int edgeNum; int *mark; int *indegree; Graph(int vertexNum){ this->vertexNum = vertexNum; edgeNum = 0; mark = new int[vertexNum]; indegree = new int[vertexNum]; for(int i = 0;i < vertexNum;i++){ mark[i] = UNVISITED; indegree[i] = 0;; } } ~Graph(){ delete[] mark; delete[] indegree; } virtual Edge getFirstEdge(int vertex) = 0;//以下四个方法供子类覆写 virtual Edge getNextEdge(Edge e) = 0; virtual void setEdge(int from,int to,int weight) = 0; virtual void delEdge(int from, int to) = 0; int toVertex(Edge e){ return e.to; } int fromVertex(Edge e){ return e.from; } int getWeight(Edge e){ return e.weight; } int getVertexNum(){ return vertexNum; } bool isEdge(Edge e){ if(e.weight > 0 && e.weight < INFINITY && e.to >= 0) return true; return false; } }; class ListGraph:public Graph{//邻接表图类定义 private: HeadNode<EdgeInfo> * graList; public: ListGraph(int vertexNum):Graph(vertexNum){ graList = new HeadNode<EdgeInfo>[vertexNum]; } Edge getFirstEdge(int vertex){ Edge e; e.from = vertex; Node<EdgeInfo> * temp = graList[vertex].head; if(temp->next != NULL){ e.to = temp->next->element.vertex; e.weight = temp->next->element.weight; } return e; } Edge getNextEdge(Edge e){ Edge nextEdge; nextEdge.from = e.from; Node<EdgeInfo> *temp = graList[e.from].head; while(temp->next != NULL && temp->next->element.vertex <= e.to){ temp = temp->next; } if(temp->next != NULL){ nextEdge.to = temp->next->element.vertex; nextEdge.weight = temp->next->element.weight; } return nextEdge; } void setEdge(int from,int to,int weight){ Node<EdgeInfo> *temp = graList[from].head; while(temp->next != NULL && temp->next->element.vertex < to){ temp = temp->next; } if(temp->next == NULL){ Node<EdgeInfo> *newEdge = new Node<EdgeInfo>(NULL); newEdge->element.vertex = to; newEdge->element.weight = weight; temp->next = newEdge; edgeNum++; indegree[to]++; return; } if(temp->next->element.vertex > to){ Node<EdgeInfo> *tempNext = temp->next; Node<EdgeInfo> *newEdge = new Node<EdgeInfo>(tempNext); newEdge->element.vertex = to; newEdge->element.weight = weight; temp->next = newEdge; edgeNum++; indegree[to]++; return; } if(temp->next->element.vertex == to){ temp->next->element.weight = weight; return; } } void delEdge(int from, int to){ Node<EdgeInfo> *temp = graList[from].head; while(temp->next != NULL && temp->next->element.vertex < to){ temp = temp->next; } if(temp->next == NULL) return; if(temp->next->element.vertex > to) return; if(temp->next->element.vertex == to){ Node<EdgeInfo> *tempNext = temp->next->next; delete temp->next; temp->next = tempNext; edgeNum--; indegree[to]--; return; } } }; class Dist{//定义最短路径信息类 public: int index; int length; int pre; }; int minVertex(Graph &g,Dist * &d){ int v; for(int i = 0;i < g.getVertexNum();i++){ if(g.mark[i] == UNVISITED){ v = i; break; } } for(int i = 0;i < g.getVertexNum();i++){ if(g.mark[i] == UNVISITED && d[i].length < d[v].length) v = i; } return v; } void addEdgetoMst(Edge e,Edge *mst,int index){ mst[index] = e; } void prim(Graph& g,int s,Edge * &mst){ int mstNum = 0; mst = new Edge[g.getVertexNum() - 1]; Dist *d; d = new Dist[g.getVertexNum()]; for(int i = 0;i < g.getVertexNum();i++){ g.mark[i] = UNVISITED; d[i].index = i; d[i].length = INFINITY; d[i].pre = s; } d[s].length = 0; g.mark[s] = VISITED; int v = s; for(int i = 0;i < g.getVertexNum() - 1;i++){ if(d[v].length == INFINITY) return; for(Edge e = g.getFirstEdge(v);g.isEdge(e);e = g.getNextEdge(e)){ if(g.mark[g.toVertex(e)] != VISITED && (d[g.toVertex(e)].length > e.weight)){ d[g.toVertex(e)].length = e.weight; d[g.toVertex(e)].pre = v; } } v = minVertex(g,d); g.mark[v] = VISITED; Edge edge(d[v].pre,d[v].index,d[v].length); addEdgetoMst(edge,mst,mstNum++); } } int _tmain(int argc, _TCHAR* argv[]) { while(true){ cout << "----------------------------------------------------" << endl; cout << "->请输入您要创建的图的结点个数." << endl; int vertexNum = 0; cin >> vertexNum; ListGraph g(vertexNum); cout << "->请输入图中所有点关联的边的起始点 终点 权重.输入-1结束" << endl; cout << "->例:若输入0 3 5则表示插入一条边,边的起点为0,终点为3,权重为5" << endl; cout << "->例:若输入-1则表示所有边已录入完毕" << endl; int startVertex; int endVertex; int weight; while(true){ cin >> startVertex; if(startVertex == -1) break; cin >> endVertex; cin >> weight; g.setEdge(startVertex,endVertex,weight); g.setEdge(endVertex,startVertex,weight); } Edge *mst; prim(g,0,mst); cout << "->最小生成树的边集为:" << endl; for(int i = 0;i < g.getVertexNum() - 1;i++){ cout << mst[i].from << " " << mst[i].to << " " << mst[i].weight << endl; } cout << "----------------------------------------------------" << endl; } }