图结构系列—基于邻接矩阵的图实现

来源:互联网 发布:学生开淘宝店 编辑:程序博客网 时间:2024/05/22 19:40

在数据结构中跌跌撞撞的走到了图这个部分,回想在几个星期前还在为各种树的各种结构定义与算法实现苦恼,忽然间已经来到了数据结构中貌似是最重要的一个结构了。在之前就有听说图在各行各业的理论研究上用途很广,如人工智能,寻路,更让我想不到的还有化合物的鉴定......可见图的未知的应用还有多少了。

从逻辑上分,图分为有向图与无向图。从存储表示上可以有着重用顶点表示一个图的,当然也有着重用边来表示一个图的。我们现在只学习了着重用顶点来表示一个有向图,下面就来详述。

用顶点表示的图又可以分为基于邻接矩阵的形式,基于邻接表的形式。他们之间各有利弊吧,比如在游戏编程中用到的用于处理各种逻辑先后顺序,一般用的是基于邻接矩阵的图,这种一般用于图不经常动的情况下。如果一个图经常的要对定点,边信息进行更新的话,那么还是用基于邻接表的图更好一些,因为链表本来易于更新。

在这里实现的是一个基于邻接矩阵标志的带权有向图,用到了模板,顶点是一种类型,边的信息又是一种类型,所以出现了template<class T,class E>的形式。用基于邻接矩阵的表示可以很方便的表示一个有向图,只需把矩阵做成不对称的就行了。但是如果是基于邻接表的存储方式,那么要出现一个出表,一个如表,在表示有向图上麻烦了许多。这个在后序文章中详细叙述。

先来看代码:

#pragma once#include<iostream>#include<fstream>using namespace std;const int DefaultVerticesNum = 30;template<class T, class E>class GraphAdjMatrix{public:        GraphAdjMatrix(const E &maxweight, int size = DefaultVerticesNum); //构造一个空图 配置用于表示两点之间不可达的信息~GraphAdjMatrix(); //销毁图T GetVertex(int vertex_idx); //根据顶点索引返回顶点元素值 也会是自定义的类对象E GetWeigth(const T &begvtx, const T &endvtx); //获取目标对象begvtx,endvtx之间的权重值T GetFirstNeighbor(const T &vertex); //获取以vertex为顶点的第一个邻接顶点T GetNextNeighbor(const T &vertex, const T &adj_vertex); //获vertex的邻接顶点adj_vertex的下一个邻接顶点bool InsertVertex(const T &vertex); //插入一个顶点bool InsertEdge(const T &begvtx, const T &endvtx, const E &cost); //在顶点begvtex,endvtx之间插入一条边(插入权重值)bool RemoveVertex(const T &vertex); //删除一个顶点bool RemoveEdge(const T &begvtx, const T &endvtx); //删除begvtx,endvtx两顶点之间的边template<class T, class E> friend istream& operator>>(istream &input, GraphAdjMatrix<T,E> &G); //重载输入template<class T, class E> friend ostream& operator<<(ostream &output, GraphAdjMatrix<T,E> &G); //重载输出template<class T, class E> friend ifstream& operator>>(ifstream &finput, GraphAdjMatrix<T,E> &G); //重载文件输入template<class T, class E> friend ofstream& operator<<(ofstream &foutput, GraphAdjMatrix<T,E> &G); //重载文件输出protected:int m_maxVerticesNum; //一个图的最大顶点数目int m_numEdge; //当前边数目int m_numVertices; //当前定点数目static const int s_NULLidx = -1; //用于指示无效的索引位置T *m_verticesList; //用于存储顶点的数组E **m_edge; //用于存储边的二维方阵E m_maxWeight; //用于表示边的无穷大 即为两顶点之间无穷远的标志int GetVerticesPos(const T &vertex); //根据顶点返回在verticesList中的索引值 不对外};template<class T, class E>GraphAdjMatrix<T,E>::GraphAdjMatrix(const E &maxweight, int size){m_maxVerticesNum = size;m_numVertices = 0;m_numEdge = 0;m_maxWeight = maxweight;m_verticesList = new T[m_maxVerticesNum];m_edge = new E * [m_maxVerticesNum];for(int i = 0; i < m_maxVerticesNum; i++){m_edge[i] = new E[m_maxVerticesNum]; //开辟边空间}for(int row = 0; row < m_maxVerticesNum; row++){for(int col = 0; col < m_maxVerticesNum; col++){m_edge[row][col] = (row == col) ? 0 : m_maxWeight; //边的初始化}}}template<class T, class E>GraphAdjMatrix<T,E>::~GraphAdjMatrix(){delete []m_verticesList;for(int idx = 0; idx < m_maxVerticesNum; idx++){delete []m_edge[idx];}delete []m_edge; //释放空间}template<class T, class E>int GraphAdjMatrix<T,E>::GetVerticesPos(const T &vertex){for(int idx = 0; idx < m_numVertices; idx++){if(m_verticesList[idx] == vertex){return idx; //找到返回索引值}}return s_NULLidx; //没找到 返回标志-1}template<class T,class E>T GraphAdjMatrix<T,E>::GetVertex(int vertex_idx) //参数为 索引值{if((0 <= vertex_idx) && (vertex_idx <= m_numVertices - 1)){return m_verticesList[vertex_idx]; //返回列表里的顶点值}else{return; //如果索引不合适就返回空}}template<class T, class E>E GraphAdjMatrix<T,E>::GetWeigth(const T &begvtx, const T &endvtx){int begidx = GetVerticesPos(begvtx);int endidx = GetVerticesPos(endvtx);if((begidx != s_NULLidx) && (endidx != s_NULLidx)){return m_edge[begidx][endidx]; //如果两个顶点都在顶点列表里面则返回之间的边的权值}else{return; //如果有一个顶点不在顶点列表里面 则返回NULL}}template<class T, class E>T GraphAdjMatrix<T,E>::GetFirstNeighbor(const T &vertex){int idx = GetVerticesPos(vertex);if(idx != s_NULLidx) //首先判断该顶点是否在图结点中{for(int col = 0; col < m_maxVerticesNum; col++) //在图结点中遍历{if((0 < m_edge[idx][col]) && (m_edge[idx][col] < m_maxWeight)) //找到第一个该结点的邻接结点就返回该邻接结点{return m_verticesList[col];}}}return; //如果该顶点不在图中或是该顶点没有邻接顶点就返回空}template<class T, class E>T GraphAdjMatrix<T,E>::GetNextNeighbor(const T &vertex, const T &adj_vertex){int idx = GetVerticesPos(vertex);int adj_idx = GetVerticesPos(adj_vertex);if((idx != s_NULLidx) && (adj_idx != s_NULLidx)){for(int col = adj_idx + 1; col < m_maxVerticesNum; col++){if((0 < m_edge[idx][adj_idx]) && (m_edge[idx][adj_idx] < m_maxWeight)){return m_verticesList[col];}}}return;}template<class T, class E>bool GraphAdjMatrix<T,E>::InsertVertex(const T &vertex){if(m_numVertices == m_maxVerticesNum){return false; //顶点已满,不能再插入}m_verticesList[m_numVertices++] = vertex; //在顶点列表的最后面插入一个顶点元素return true;}template<class T, class E>bool GraphAdjMatrix<T,E>::InsertEdge(const T &begvtx, const T &endvtx, const E &cost){int begidx = GetVerticesPos(begvtx);int endidx = GetVerticesPos(endvtx);if((begidx != s_NULLidx) && (endidx != s_NULLidx) && (m_edge[begidx][endidx] == m_maxWeight)) //满足插入边条件{m_edge[begidx][endidx] = cost;m_numEdge++;return true;}return false; //不满足边插入条件就返回false}template<class T, class E>bool GraphAdjMatrix<T,E>::RemoveVertex(const T &vertex){int idx = GetVerticesPos(vertex);if(idx == s_NULLidx){return false; //要删除的顶点不在图中}if(m_numVertices == 1){return false; //图中仅剩下一个顶点时候不能删除}m_verticesList[idx] = m_verticesList[m_numVertices - 1]; //把顶点列表里最后一个顶点赋给待删除的顶点位置上for(int row = 0; row < m_numVertices; row++){if((0 < m_edge[row][idx]) && (m_edge[row][idx] < m_maxWeight)){m_numEdge--; //只要是和待删除顶点有关联的边全部干掉}}for(int row = 0; row < m_numVertices; row++){m_edge[row][idx] = m_edge[row][m_numVertices - 1]; //该方阵的最后一列去填充待删除的那列m_edge[row][m_numVertices - 1] = m_maxWeight; //将最后一列置为无限远}m_numVertices--; //定点数目减掉一个for(int col = 0; col < m_numVertices; col++){m_edge[idx][col] = m_edge[m_numVertices][col]; //将最后一行赋给待删除行m_edge[m_numVertices][col] = m_maxWeight; //置为最远边距离}return true;}template<class T, class E>bool GraphAdjMatrix<T,E>::RemoveEdge(const T &begvtx, const T &endvtx){int begidx = GetVerticesPos(begvtx);int endidx = GetVerticesPos(endvtx);if((begidx != s_NULLidx) && (endidx != s_NULLidx)&& (0 < m_edge[begidx][endidx]) && (m_edge[begidx][endidx] < m_maxWeight)){m_edge[begidx][endidx] = m_edge[endidx][begidx] = m_maxWeight;m_numEdge--;return true;}return false;}template<class T, class E>istream& operator>> (istream &input, GraphAdjMatrix<T,E> &G) //重载输入{}template<class T, class E>ostream& operator<< (ostream &output, GraphAdjMatrix<T,E> &G) //重载输出{int verticesNum = G.m_numVertices;int edgesNum = G.m_numEdge;output<<"顶点数目: "<<verticesNum<<endl;output<<"边数目: "<<edgesNum<<endl;T Tbegvtx, Tendvtx;E Eedge;for(int row = 0; row < verticesNum; row++){for(int col = 0; col < verticesNum; col++){Tbegvtx = G.GetVertex(row);Tendvtx = G.GetVertex(col);Eedge = G.GetWeigth(Tbegvtx, Tendvtx);output<<"<"<<Tbegvtx<<","<<Tendvtx<<">  "<<Eedge<<endl;}}return output;}template<class T, class E>ifstream& operator>> (ifstream &finput, GraphAdjMatrix<T,E> &G) //重载文件输入{int initializeVerticesNum; //初始化顶点数目finput>>initializeVerticesNum;T Tvalue;for(int i = 0; i < initializeVerticesNum; i++){finput>>Tvalue;G.InsertVertex(Tvalue);}int initializeEdgeNum; //初始化边数目finput>>initializeEdgeNum;T Tbegvtx, Tendvtx;E Eedge;int begidx, endidx;for(int i = 0; i < initializeEdgeNum; i++){finput>>Tbegvtx>>Tendvtx>>Eedge;begidx = G.GetVerticesPos(Tbegvtx);endidx = G.GetVerticesPos(Tendvtx);if((begidx != G.s_NULLidx) && (endidx != G.s_NULLidx)){G.InsertEdge(Tbegvtx, Tendvtx, Eedge);}}return finput;}template<class T, class E>ofstream& operator<< (ofstream &foutput, GraphAdjMatrix<T,E> &G) //重载文件输出{}

在写模板的时候有一个问题,那就是如果我搜索图的时候没有找到指定的顶点信息或是边信息,返回怎么办,要返回什么呢?

所以在这里就直接忽略了这种情况了。所以只有return 返回的是无。

下面是主函数:

#include"GraphAdjMatrix.h"#include<iostream>#include<fstream>#include"Direction.h"#include"City.h"using namespace std;void main(){GraphAdjMatrix<City, int> gh(-1);ifstream in("GraphAdjMatrix.txt");in>>gh;cout<<"一个图信息如下:"<<endl;cout<<gh<<endl;}

构造函数中的-1表示两点之间不可达的无穷远表示


由于代码中的注释已经很详细了,在这里就不多说,看下面一个运行截图:



自定义类型是一个城市类型,有一些必要的重载,输入输出还有运算符重载等。