图的 Prim算法和Dijkstra算法
来源:互联网 发布:particle fever 知乎 编辑:程序博客网 时间:2024/04/30 04:08
用矩阵形式实现图的两个算法
1) 无向图中,使用Prim算法,构建最小生成树
2) 有向图中,使用DijKstra算法,得到单源最短路径
首先构建一下图,这里是用矩阵实现的
Graph类简述:
numVertex 是图中当前的节点数
numEdge 是图中当前的边数
maxNumVertex 表示未自增长前的矩阵中节点最大值
maxNumEdge 表示未自增长前的矩阵中边最大值
使用一维数组NodeList[]来存储节点信息
使用一维数组AdjMatrix[]来存放邻接表 AdjMatrix[I * maxNumVertex + j] 表示i号节点到j节点的有向路径的长度如下图
设MAX为9999 初始化时两点表示不可达
设MAX_EDGETYPE为9999 表示边的无穷大 也表示不可达
主要代码
Graph.hpp
#include <iostream>using namespace std;#define DEFAULT_VERTEX_NUM 5#define MAX 99999#define MAX_EDGETYPE 99999template<class VertexType,class EdgeType>class Graph{VertexType *NodeList; //保存顶点数据的一位数组EdgeType *AdjMatrix; //保存邻接矩阵的一维数组,邻接矩阵 //AdjMatrix[i*NumVertex + j] ,表示 //i顶点到j顶点方向,数值表示路径长度int currentNode; //指着当前的节点int numEdge; //图当前的边数int maxNumEdge; //图的最大边数int numVertex; //图当前的顶点数int maxNumVertex; //图最大的顶点数public:Graph(int );~Graph();int GetNumVertex(){return numVertex;}VertexType InsertVertex(const VertexType& ); //插入一个节点int RemoveVertex(const VertexType& ); //删除一个节点EdgeType InsertEdge(int i,int j,EdgeType weight); //从i向j插入一条权重为weight的边int RemoveEdge(int i ,int j); //移除i向j的边void ExpandSize(); //用于动态自增长,节点数翻倍、同时边数相应翻两倍/*传入一个初始的的顶点,进行Prim算法来构建最小生成树构建方式用一维数组表示 如PrimTree[i*numVertex + j], *由于Graph类设计的时候考虑到了有向图的情况,但是在本算法中并不使用有向图,所以上面的PrimTree[i*numVertex + j] *也等同于PrimTree[j*numVertex + i]. 本函数返回的就是这样的一个数组*/EdgeType *Prim(VertexType startVertexValue);EdgeType *Dijkstra(VertexType startVertexValue);void Cout();};//测试输出template<class VertexType,class EdgeType>void Graph<VertexType, EdgeType>::Cout(){cout << "NodeList 数组索引:\n";cout <<" i\\j";for(int i = 0;i < numVertex;i++)cout << "\t" << i << " ";int cntLine = 0; //列计数int cntRow = 0; //行计数//cout << "\n邻接矩阵数据:\n";cout << endl;for(int i = 0;i < maxNumEdge;i++){if( i % maxNumVertex == 0 && cntRow < numVertex )cout << "\t" << cntRow++;if(cntRow == numVertex && cntLine == numVertex )break;if(cntLine++ < numVertex && cntRow <= numVertex)cout << "\t" << AdjMatrix[i] ;if( (i + 1) % maxNumVertex == 0){ //每行遍历结束换行cout << endl;cntLine = 0;}}cout << endl;cout << "各节点的值: \n";for(int i = 0;i < numVertex;i++)cout << NodeList[i] << " ";cout << "\n\n";}//动态自增长template<class VertexType,class EdgeType>void Graph<VertexType, EdgeType>::ExpandSize(){//复制所有节点数组到扩容后的NodeListVertexType *tempNodeList = NodeList;int originMaxNumVertex = maxNumVertex;maxNumVertex <<= 1;NodeList = new VertexType[maxNumVertex];for(int i = 0;i < (maxNumVertex >> 1);i++)NodeList[i] = tempNodeList[i];delete []tempNodeList;//复制邻接边数组AdjMatrixEdgeType *tempAdjMatrix = AdjMatrix;//int originMaxNumEdge = maxNumEdge;maxNumEdge = maxNumVertex * maxNumVertex;AdjMatrix = new EdgeType[maxNumEdge];for(int i = 0;i < originMaxNumVertex;i++)for(int j = 0;j < originMaxNumVertex;j++)AdjMatrix[maxNumVertex * i + j] = tempAdjMatrix[originMaxNumVertex * i + j];//未知变量都赋为MAX表示不可达,修改对角线元素为0for(int i = 0;i < maxNumVertex;i++)for(int j = 0;j < maxNumVertex;j++){if(!(i < originMaxNumVertex && j < originMaxNumVertex)){AdjMatrix[maxNumVertex * i + j] = MAX;}}for(int i = originMaxNumVertex;i < maxNumVertex;i++){AdjMatrix[maxNumVertex * i + i] = 0;}delete []tempAdjMatrix;}template<class VertexType,class EdgeType>VertexType Graph<VertexType, EdgeType>::InsertVertex(const VertexType& newVertexValue){if(numVertex == maxNumVertex)ExpandSize();NodeList[numVertex++] = newVertexValue;return newVertexValue;}template<class VertexType,class EdgeType>int Graph<VertexType, EdgeType>::RemoveVertex(const VertexType& vertexValue){if(numVertex == 0)return -1; //没有找到else{int pos = 0;for(;pos < numVertex;pos++){if(NodeList[pos] == vertexValue)break;}if(pos == numVertex)return -1; //没有找到for(int i = pos;i < numVertex;i++){NodeList[i] = NodeList[i + 1];}for(int i = 0;i < maxNumVertex - 1;i++)for(int j = 0;j < maxNumVertex - 1;j++){int x = j >= pos ? j + 1 : j;int y = i >= pos ? i + 1 : i;AdjMatrix[maxNumVertex * i + j] = AdjMatrix[maxNumVertex * y + x];}return --numVertex;}}template<class VertexType,class EdgeType>EdgeType Graph<VertexType, EdgeType>::InsertEdge(int i,int j,EdgeType weight){if(i < maxNumVertex && j < maxNumVertex && i != j)AdjMatrix[maxNumVertex * i + j] = weight;return weight;}template<class VertexType,class EdgeType>int Graph<VertexType, EdgeType>::RemoveEdge(int i,int j){if(i < maxNumVertex && j < maxNumVertex && i != j){AdjMatrix[maxNumVertex * i + j] = MAX;numEdge--;return numEdge;}elsereturn -1;}template<class VertexType,class EdgeType>Graph<VertexType,EdgeType>::Graph(int maxVertexSize = DEFAULT_VERTEX_NUM){numVertex = 0;numEdge = 0;currentNode = 0;maxNumVertex = maxVertexSize > DEFAULT_VERTEX_NUM ? maxVertexSize : DEFAULT_VERTEX_NUM;maxNumEdge = maxNumVertex * maxNumVertex;AdjMatrix = new EdgeType[maxNumEdge];NodeList = new VertexType[maxNumVertex];// 对角线顶点初始化为0,非对角线初始为MAX表示不可达for(int i = 0;i < maxNumEdge;i++)AdjMatrix[i] = MAX;for(int i = 0;i < maxNumVertex;i++)AdjMatrix[i + maxNumVertex * i] = 0;}template<class VertexType,class EdgeType>Graph<VertexType,EdgeType>::~Graph(){delete []AdjMatrix;delete []NodeList;}template<class VertexType,class EdgeType>EdgeType* Graph<VertexType,EdgeType>::Prim(VertexType startVertexValue){//初始化PrimTree所有点之间都不可达EdgeType *PrimTree = new EdgeType[numVertex * numVertex];for(int i = 0;i < numVertex;i++)for(int j = 0;j < numVertex;j++){PrimTree[i * numVertex + j] = MAX_EDGETYPE;}int startPos = -1;for(int i = 0;i < numVertex;i++){if(NodeList[i] == startVertexValue){startPos = i;break;}}if(startPos == -1) //如果没有找到,就返回NULLreturn NULL;//isChosen数组记录顶点i是否被取出bool *isChosen = new bool[numVertex];for(int i = 0;i < numVertex;i++)isChosen[i] = false;//选出指定的头结点,放入顶点集U,startPos是起始顶点isChosen[startPos] = true;int lastPos = startPos; //作图的时候保留每一次绘制边的起始顶点int nextPos; //作图的时候保留每一次绘制边的终止顶点int newPos = startPos; //新加入顶点的定位for(int i = 0;i < numVertex - 1;i++){ //把所有节点都包括进来,前提是一副连通图EdgeType min = MAX_EDGETYPE;/* 找到在U-V中仍未选入的第一个顶点 *每一次有新节点加入的时候都要定位在新节点,首先由到新节点的权值最小,选择下一个加入的顶点*/for(int j = 0;j < numVertex;j++){//如果某个顶点还未被选走if(!isChosen[j]){EdgeType weight = AdjMatrix[newPos * maxNumVertex + j] < AdjMatrix[j * maxNumVertex + newPos] ?AdjMatrix[newPos * maxNumVertex + j] : AdjMatrix[j * maxNumVertex + newPos];if(weight < min){min = weight;nextPos = j;}}}//还原min的初始值min = MAX_EDGETYPE;for(int j = 0;j < numVertex;j++){if(isChosen[j]){ //在已经被选走的顶点中找到距离上面未加入顶点权值最小的顶点,作为画边的起始顶点EdgeType weight = AdjMatrix[j * maxNumVertex + nextPos] < AdjMatrix[nextPos * maxNumVertex + j] ?AdjMatrix[j * maxNumVertex + nextPos] : AdjMatrix[nextPos * maxNumVertex + j];if(weight < min){min = weight;lastPos = j;}}}PrimTree[lastPos * numVertex + nextPos] = min;isChosen[nextPos] = true;newPos = nextPos; //newPos 每次都指向刚刚新加入的顶点}return PrimTree;}template<class VertexType,class EdgeType>EdgeType* Graph<VertexType,EdgeType>::Dijkstra(VertexType startVertexValue){EdgeType *DijkstraPath = new EdgeType[numVertex];int startPos = -1;for(int i = 0;i < numVertex;i++){if(NodeList[i] == startVertexValue){startPos = i;break;}}if(startPos == -1) //如果没有找到,就返回NULLreturn NULL;//记录源点到各顶点的最短路径for(int i = 0;i < numVertex;i++)DijkstraPath[i] = AdjMatrix[startPos * maxNumVertex + i];//isChosen数组记录顶点i是否被取出bool *isChosen = new bool[numVertex];for(int i = 0;i < numVertex;i++)isChosen[i] = false;//初始顶点加入isChosen[startPos] = true;EdgeType min;int nextPos;//修改startPos有向可达的图DijkstraGraph[]for(int i = 0;i < numVertex - 1;i++){min = MAX_EDGETYPE;for(int j = 0;j < numVertex;j++){EdgeType weight = AdjMatrix[startPos * maxNumVertex + j];if(!isChosen[j]){if(weight < min){min = weight;nextPos = j;}}}//取走nextPosisChosen[nextPos] = true;//每次有新的点加入就更新DijkstraGraph[]for(int j = 0;j < numVertex;j++){EdgeType D_startToNext = AdjMatrix[startPos * numVertex + nextPos];EdgeType D_NextToJ = AdjMatrix[nextPos * numVertex + j];EdgeType D_startToJ = AdjMatrix[startPos * numVertex + j];if(D_startToNext + D_NextToJ < D_startToJ)DijkstraPath[j] = D_startToNext + D_NextToJ;}}return DijkstraPath;}
一. 先做了基本成员函数的正确性验证:
1.插入和删除节点
InsertVertex(const EdgeType&) 和 RemoveVertex(const EdgeType &)
2. 插入和删除边
InsertEdge(int i,int j,EdgeType weight) 和 RemoveEdge(int i,int j)
3 增加节点的时候动态增长
ExpandSize()
成功的在Insert的时候动态增长了原先的节点总数,和邻接表AdjMatrix的规模
测试代码:
int main(){Graph<int,int> g;for(int i = 0;i < 6;i++){g.InsertVertex(i);}g.InsertEdge(2,3,23);g.InsertEdge(2,4,24);g.InsertEdge(3,4,34);g.InsertEdge(1,5,15);g.Cout();g.RemoveVertex(2);g.Cout();return 0;}
得到输出:
1)成功的用InsertVertex() 和InsertEdge()函数插入了新的节点和新的边
2)删除了节点值为int 2 的点,并且对原先的两个数组进行了重购索引 2 之后的节点填充了NodeList[]和AdjMatrix[]中原来[2]的位置
二. Prim算法实现简述:
Prim函数代码:
template<class VertexType,class EdgeType>EdgeType* Graph<VertexType,EdgeType>::Prim(VertexType startVertexValue){//初始化PrimTree所有点之间都不可达EdgeType *PrimTree = new EdgeType[numVertex * numVertex];for(int i = 0;i < numVertex;i++)for(int j = 0;j < numVertex;j++){PrimTree[i * numVertex + j] = MAX_EDGETYPE;}int startPos = -1;for(int i = 0;i < numVertex;i++){if(NodeList[i] == startVertexValue){startPos = i;break;}}if(startPos == -1) //如果没有找到,就返回NULLreturn NULL;//isChosen数组记录顶点i是否被取出bool *isChosen = new bool[numVertex];for(int i = 0;i < numVertex;i++)isChosen[i] = false;//选出指定的头结点,放入顶点集U,startPos是起始顶点isChosen[startPos] = true;int lastPos = startPos; //作图的时候保留每一次绘制边的起始顶点int nextPos; //作图的时候保留每一次绘制边的终止顶点int newPos = startPos; //新加入顶点的定位for(int i = 0;i < numVertex - 1;i++){ //把所有节点都包括进来,前提是一副连通图EdgeType min = MAX_EDGETYPE;/* 找到在U-V中仍未选入的第一个顶点 *每一次有新节点加入的时候都要定位在新节点,首先由到新节点的权值最小,选择下一个加入的顶点*/for(int j = 0;j < numVertex;j++){//如果某个顶点还未被选走if(!isChosen[j]){EdgeType weight = AdjMatrix[newPos * maxNumVertex + j] < AdjMatrix[j * maxNumVertex + newPos] ?AdjMatrix[newPos * maxNumVertex + j] : AdjMatrix[j * maxNumVertex + newPos];if(weight < min){min = weight;nextPos = j;}}}//还原min的初始值min = MAX_EDGETYPE;for(int j = 0;j < numVertex;j++){if(isChosen[j]){ //在已经被选走的顶点中找到距离上面未加入顶点权值最小的顶点,作为画边的起始顶点EdgeType weight = AdjMatrix[j * maxNumVertex + nextPos] < AdjMatrix[nextPos * maxNumVertex + j] ?AdjMatrix[j * maxNumVertex + nextPos] : AdjMatrix[nextPos * maxNumVertex + j];if(weight < min){min = weight;lastPos = j;}}}PrimTree[lastPos * numVertex + nextPos] = min;isChosen[nextPos] = true;newPos = nextPos; //newPos 每次都指向刚刚新加入的顶点}return PrimTree;}
步骤:
1)由传入的顶点值参数找到顶点startPos
2)为了绘制边,需要lastPos记录边起始顶点,nextPos 记录边终止顶点
3)循环遍历所有顶点,直到所有顶点加入到已选取集V为止
4)每次选取待加入顶点时,未加入V的顶点都需要通过和前一次循环刚加入的新顶点newPos,以newPos为起始点进行两个节点之间的权值比较选取权值最小一条边,该边的终点的顶点索引就是nextPos
5)得到的nextPos还必须和已选取集V中所有顶点比较,选择在这之中权值最小的边,得到一个lastPos为要新加入的边起始点,nextPos为该边的终点,在PrimTree中绘制
PrimTree[lastPos * numVertex + nextPos] ,同时把nextPos加入已选取集V
原始图:
最小生成树的图:
测试代码main.cpp
int main(){Graph<int,int> g;for(int i = 0;i < 6;i++){g.InsertVertex(i);}//初始化图g.InsertEdge(0,1,3);g.InsertEdge(0,2,1);g.InsertEdge(1,2,2);g.InsertEdge(1,3,4);g.InsertEdge(2,3,2);g.InsertEdge(3,4,3);g.InsertEdge(3,5,4);g.InsertEdge(4,5,1);cout << "原始图:" << endl;g.Cout();cout << "\n\n最小生成树的图: " << endl;//Prim构建最小生成树int numVertex = g.GetNumVertex();int *PrimTree = g.Prim(0);for(int i = 0;i < numVertex;i++)cout << "\t" << i;cout << endl;for(int i = 0,cntLine = 0;i < numVertex;i++){cout << "" << cntLine++;for(int j = 0;j < numVertex;j++){cout << "\t" << PrimTree[i * numVertex + j];}cout << endl;}cout << endl;return 0;}
测试输出:(使用数组实现图,因为设计Graph类的时候考虑了有向性,但是在实现Prim算法的时候没有用到方向
所以在数组中AdjMatrix[i][j] 和 AdjMatrix[j][i] 二者是相同的,二选一即可,99999是MAX值表示不可达!
三. Dijkstra算法实现简述
Dijkstra代码:
template<class VertexType,class EdgeType>EdgeType* Graph<VertexType,EdgeType>::Dijkstra(VertexType startVertexValue){EdgeType *DijkstraPath = new EdgeType[numVertex];int startPos = -1;for(int i = 0;i < numVertex;i++){if(NodeList[i] == startVertexValue){startPos = i;break;}}if(startPos == -1) //如果没有找到,就返回NULLreturn NULL;//记录源点到各顶点的最短路径for(int i = 0;i < numVertex;i++)DijkstraPath[i] = AdjMatrix[startPos * maxNumVertex + i];//isChosen数组记录顶点i是否被取出bool *isChosen = new bool[numVertex];for(int i = 0;i < numVertex;i++)isChosen[i] = false;//初始顶点加入isChosen[startPos] = true;EdgeType min;int nextPos;//修改startPos有向可达的图DijkstraGraph[]for(int i = 0;i < numVertex - 1;i++){min = MAX_EDGETYPE;for(int j = 0;j < numVertex;j++){EdgeType weight = AdjMatrix[startPos * maxNumVertex + j];if(!isChosen[j]){if(weight < min){min = weight;nextPos = j;}}}//取走nextPosisChosen[nextPos] = true;//每次有新的点加入就更新DijkstraGraph[]for(int j = 0;j < numVertex;j++){EdgeType D_startToNext = AdjMatrix[startPos * numVertex + nextPos];EdgeType D_NextToJ = AdjMatrix[nextPos * numVertex + j];EdgeType D_startToJ = AdjMatrix[startPos * numVertex + j];if(D_startToNext + D_NextToJ < D_startToJ)DijkstraPath[j] = D_startToNext + D_NextToJ;}}return DijkstraPath;}
步骤:
1)由传入的顶点值参数找到顶点startPos
2)先把顶点startPos可达的顶点和权值赋值给DijkstraPath[]
3)循环遍历所有顶点,直到所有顶点加入到已选取集V为止
4)每次选取待加入顶点时,未加入V的顶点都需要通过比较初始顶点到达新顶点的路径长度,选择最小路径长度的新顶点,作为下一个要处理的顶点nextPos
5) 得到的nextPos后,重新比较开始顶点到nextPos顶点:D_startToNext ; nextPos顶点到j顶点:D_NextToJ 以及 开始顶点startPos到j D_startToJ的路径长度比较,如果由于新插入的点,即nextPos顶点使得起始顶点startPos到顶点j的路径变短,那么就重新对startPos到j的Dijkstra[j] 重新赋值。
原始图:
测试代码:
/******************Dijkstra算法测试********************************/int main(){Graph<int,int> g;for(int i = 0;i < 5;i++){g.InsertVertex(i);}//初始化图g.InsertEdge(0,1,10);g.InsertEdge(1,2,50);g.InsertEdge(3,2,20);g.InsertEdge(3,4,30);g.InsertEdge(0,3,30);g.InsertEdge(2,4,10);g.InsertEdge(0,4,100);cout << "原始有向图:" << endl;g.Cout();int startVertexValue = 0;int numVertex = g.GetNumVertex();int *DijkstraPath = g.Dijkstra(startVertexValue);cout << endl << "由startPos的Dijkstra有向可达路径图:\n";cout << "\t";for(int i = 0;i < numVertex;i++)cout << "\t" << i;cout << endl;cout << "\t" << startVertexValue;for(int i = 0;i < numVertex;i++)cout << "\t" <<DijkstraPath[i] ;return 0;}
测试输出:
上图中,0是起始顶点,即startPos 到顶点1 2 3 4 的路径长度分别是10 、 50、30、60
- 图的 Prim算法和Dijkstra算法
- Prim算法和Dijkstra算法的异同
- Dijkstra和Prim算法的区别
- Prim和Dijkstra算法的区别
- 图的邻接矩阵实现(包括PRIM和DIJKSTRA算法)
- Prim算法、Kruskal算法和Dijkstra…
- dijkstra算法与prim算法的区别
- Prim算法与Dijkstra算法的区别
- Prim算法与Dijkstra算法的区别
- dijkstra算法与prim算法的区别
- Prim算法与Dijkstra算法的区别
- Dijkstra算法与Prim算法的区别
- dijkstra算法与prim算法的区别
- Dijkstra算法与Prim算法的异同
- 两个极其相似的算法Prim和Dijkstra
- Dijkstra和Prim算法 【含数学证明】
- Prim算法与Dijkstra的异同
- 图有关算法(prim、kruskal、dijkstra)
- Eclipse中常用的的快捷键
- Circle类
- Linux/Ubuntu下的用户切换
- 【Jetty Server 开发系列之一】搭建Jetty Server环境&&Http客户端实现交互
- curl的ftp功能也不错
- 图的 Prim算法和Dijkstra算法
- Fibonacci堆的代码-欢迎斧正并且报告Bug
- 【C/S通信交互之Http篇】Cocos2dx(Client)使用Curl与Jetty(Server)实现手机网游Http通信框架(内含解决curl.h头文件找不到问题)
- QT 中Gtk-WARNING **: 无法在模块路径中找到主题引擎:“pixmap”?
- HITS算法解析与光年论坛的两个问题
- Java 网络通信
- Linux 安装 Mysql
- 使用DSP/BIOS数据类型
- ndk 编译android内核模块无法加载 解决方法