C++ 图的实现
图可以说是算法与数据结构中十分重要的一个部分,然而对于图的实现,还是有一点点繁琐,今天参考清华大学出版社《数据结构》一书进行了一些回顾,记录于此。
本文并不对基本概念进行过多探讨,而着眼于实现。基于对途中边集的存储有邻接矩阵以及邻接表两种主要形式。本文将着重实现三个类:Graph基类,包含大量的virtual函数以待在派生类中实现;Graph的派生类Graphmtx(邻接矩阵实现图的存储)、Graphlnk(邻接表实现图的存储)。并通过简单的test对以上实现加以测试。废话不多说,直接贴代码,代码中加了比较详细的注释说明。
- 基类Graph定义:
//FileName : Graph.h #pragma once #include<iostream> using namespace std; #define INF 100000 //表示正无穷 const int DefaultVertices = 30; template<class T,class E> class Graph { public: static const E maxWeight = INF; Graph(int size = DefaultVertices){}; ~Graph(){}; bool GraphEmpty()const //检查为空 { if (numEdges ==0 )return true; else return false; } bool GraphFull()const //检查为满 { if(numVertices==maxVertices ||numEdges==maxVertices*(maxVertices-1)/2) return true; else return false; } int NumberOfVertices(){return numVertices;} //返回当前顶点数 int NumberOfEdges(){return numEdges;} //返回当前边数 virtual T getValue(int i)=0; //取顶点i的值,i不合理返回0 virtual E getWeight(int v1,int v2)=0; //取边(v1,v2)的权值 virtual int getFirstNeighbor(int v)=0; //取顶点v的第一个邻接顶点 virtual int getNextNeighbor(int v,int w)=0; //取邻接顶点w的下一个邻接顶点 virtual bool insertVertex(const T& vertex)=0; //插入一个顶点vertex virtual bool insertEdge(int v1, int v2,E cost)=0;//插入边(v1,v2),权值cost virtual bool removeVertex(int v)=0; //删除顶点v和所有与之关联的边 virtual bool removeEdge(int v1,int v2)=0; //删除边(v1,v2) virtual int getVertexPos(T vertex)=0; protected: int maxVertices; int numEdges; int numVertices; };
- 基于邻接表实现边集存储的派生子类Graphlnk定义及实现:
//Filename : Grapglnk.h #include "Graph.h" template<class T ,class E> struct Edge //边界点的定义 { int dest; //边的另一顶点位置 E cost; //权值 Edge<T ,E> *link;//下一条边链指针 Edge(){} //构造函数 Edge(int num , E weight):dest(num),cost(weight),link(NULL){} //构造函数 bool operator != (Edge<T,E> &R)const{ //判边不等否 return (dest!=R.dest)? true:false; } }; template<class T ,class E > struct Vertex{ //顶点的定义 T data; //顶点名字 Edge<T ,E> *adj; //边链表的头指针 }; template <class T ,class E> class Graphlnk: public Graph<T,E> { public: Graphlnk(int sz=DefaultVertices); ~Graphlnk(); T getValue(int i) { return (i>=0 && i<numVertices)? NodeTable[i].data : 0; } E getWeight(int v1,int v2); int getFirstNeighbor(int v); int getNextNeighbor(int v,int w); bool insertVertex(const T& vertex); bool insertEdge(int v1, int v2,E cost); bool removeVertex(int v); bool removeEdge(int v1,int v2); void inputGraph(); void outputGraph(); int getVertexPos( T vertex){ for (int i = 0;i<numVertices;i++) { if(NodeTable[i].data == vertex) return i; } return -1; //找不到就返回-1 } private: Vertex<T,E> * NodeTable; //顶点表 }; template <class T ,class E> Graphlnk<T,E>::Graphlnk(int sz) //构造函数 { maxVertices = sz; numVertices = 0; numEdges = 0; NodeTable = new Vertex<T,E>[maxVertices]; if(NodeTable ==NULL){ cerr<<"存储分配错!"<<endl;exit(1);} for (int i = 0;i<maxVertices;i++) { NodeTable[i].adj = NULL; } } template<class T ,class E> Graphlnk<T,E>::~Graphlnk() //析构函数 { for(int i = 0; i<numVertices;i++) { Edge<T,E> *p = NodeTable[i].adj; while( p!= NULL) { NodeTable[i].adj = p->link; delete p ; p = NodeTable[i].adj; } } delete []NodeTable; } template <class T ,class E> E Graphlnk<T,E>::getWeight(int v1,int v2) //返回边(v1,v2)的权重,边不存在则返回0 { if(v1 != -1 && v2 !=-1) { Edge<T,E> *p = NodeTable[v1].adj; while(p!=NULL && p->dest!=v2) p = p->link; if(p!=NULL) return p->cost; } return 0; } template<class T,class E> int Graphlnk<T,E>::getFirstNeighbor(int v) //获得v的第一个邻接顶点,找不到则返回-1 { if(v!=-1) { Edge<T,E> *p = NodeTable[v].adj; if(p!=NULL) return p->dest; } return -1; } template<class T ,class E> int Graphlnk<T,E>::getNextNeighbor(int v,int w) //获得v的邻接顶点w的下一个邻接顶点 { if(v!=-1 ) { Edge<T,E> *p = NodeTable[v].adj; while(p!=NULL && p->dest != w) //寻找邻接顶点w p = p->link; if(p!=NULL && p->link!=NULL) //找到w且存在下一个邻接顶带你 return p->link->dest; } return -1; } template<class T ,class E> bool Graphlnk<T,E>::insertVertex(const T& vertex) //插入点 { if(numVertices == maxVertices) return false; //图已满,插入失败 NodeTable[numVertices].data = vertex; numVertices++; return true; } template<class T ,class E> bool Graphlnk<T,E>::removeVertex(int v) //删除点 { if(numVertices ==1 || v<0 ||v>=numVertices) return false; Edge<T,E> *p ,*s ,*t; int k; while(NodeTable[v].adj != NULL) //删除该顶点,以及与之邻接的顶点中的记录 { p = NodeTable[v].adj; k = p->dest; s = NodeTable[k].adj; //以找对称存放的边节点 t = NULL; while (s!=NULL && s->dest!= v) //在对称点的邻接表里面找v,删除掉 { t = s; s = s->link; } if(s!=NULL) { if(t==NULL) //第一个邻接顶点就是v NodeTable[k].adj = s->link; else t->link = s->link; delete s; } NodeTable[v].adj = p->link; delete p; numEdges --; } numVertices--; NodeTable[v].data = NodeTable[numVertices].data; p = NodeTable[v].adj = NodeTable[numVertices].adj; while(p!=NULL) { s = NodeTable[p->dest].adj; while(s!=NULL) { if(s->dest == numVertices) { s->dest = v; break; } else s = s->link; } p = p->link; } return true; } template<class T ,class E> bool Graphlnk<T,E>::insertEdge(int v1, int v2,E weight) //插入一条边,若边已存在,或参数不合理,返回false { if(v1>=0 && v1< numVertices && v2>=0 && v2< numVertices) { Edge<T,E> *q ,*p = NodeTable[v1].adj; //先检查该边是否已经存在 while(p!=NULL && p->dest!= v2) p = p->link; if(p!=NULL)//找到该边 { cout<<"该边已经存在,插入失败!"<<endl; return false; } p = new Edge<T,E>; q = new Edge<T,E>; p->dest = v2; p->cost = weight; p->link = NodeTable[v1].adj; NodeTable[v1].adj = p; //插入到邻接表表头 q->dest = v1; q->cost = weight; q->link = NodeTable[v2].adj; NodeTable[v2].adj = q; numEdges ++; return true; } return false; } template<class T, class E> bool Graphlnk<T,E>::removeEdge(int v1,int v2) { if(v1 >=0 && v1< numVertices && v2>=0 && v2< numVertices) { Edge<T,E> *p = NodeTable[v1].adj, *q = NULL ,*s = p; while(p!=NULL && p->dest!= v2) //先找该边 { q = p; p = p->link; } if(p!=NULL) //找到该边 { if(p==s)//第一个节点就找到 NodeTable[v1].adj = p->link; else q->link = p->link; delete p; } else return false; //找不到边 p = NodeTable[v2].adj; q= NULL; s = p; while(p!=NULL && p->dest!=v1) { q = p; p = p->link; } if(p==s) NodeTable[v2].adj = p->link; else q->link = p->link; delete p; return true; } return false; } template<class T ,class E> void Graphlnk<T,E>::inputGraph() { //通过从输入流对象in输入n的顶点和e条五项边的信息建立邻接矩阵表示的图G。邻接矩阵初始化工作在构造函数完成 int i,j,k,m,n; T e1,e2; E weight; cout<<"请输入顶点数和边数(空格隔开):"<<endl; cin >> n >> m; //输入点数n的边数m cout<<"请依次输入顶点:"<<endl; for(i=0;i<n;i++)//输入顶点,建立顶点表 { cin>>e1; this->insertVertex(e1); //G.insertVertex(e1); } cout<<"请依次输入边,形如 v1 v2 weight :"<<endl; i=0; while(i<m) { cin >> e1>>e2>>weight; j = this->getVertexPos(e1);//查顶点号 k = this->getVertexPos(e2); if(j==-1 || k==-1) { cout<<"边两端点信息有误,重新输入!"<<endl; } else { if(this->insertEdge(j,k,weight)) i++; } } } template<class T ,class E> void Graphlnk<T,E>::outputGraph() { //输出图的所有顶点和边信息 int i,j,n,m; T e1,e2; E weight; n = this->NumberOfVertices(); //点数 m = this->NumberOfEdges(); //边数 cout<<"顶点数的边数为:"; cout<<n<<","<<m<<endl; //输出点数和边数 cout<<"各边依次为:"<<endl; for(i=0;i<n;i++) { for(j=i+1;j<n;j++) { weight = this->getWeight(i,j); if(weight>0 && weight< maxWeight) { e1 = this->getValue(i); e2 = this->getValue(j); cout<<"("<<e1<<","<<e2<<","<<weight<<")"<<endl; } } } }
- 基于邻接矩阵实现边集存储的派生子类Graphmtx定义及实现:
//Filename : Graphmtc.h #include "Graph.h" #include<iostream> using namespace std; template<class T,class E> class Graphmtx: public Graph<T,E> { public: Graphmtx(int sz=DefaultVertices); //构造 ~Graphmtx() //析构 { delete []VerticesList; delete []Edge; } T getValue(int i) //取顶点i的值,若i不合理返回NULL { if(i>=0 && i<numVertices) return VerticesList[i]; else return NULL; } E getWeight(int v1,int v2) //取边(v1,v2)的权值,不合理返回0 { if(v1!=-1 && v2!=-1) return Edge[v1][v2]; else return 0; } int getFirstNeighbor(int v); int getNextNeighbor(int v,int w); bool insertVertex(const T& vertex); bool insertEdge(int v1, int v2,E cost); bool removeVertex(int v); bool removeEdge(int v1,int v2); void inputGraph(); void outputGraph(); int getVertexPos(T vertex) //给出顶点在图中的位置 { for(int i=0;i<numVertices;i++) if(VerticesList[i]==vertex)return i; return -1; //找不到返回-1 } private: T *VerticesList; //顶点表 E * *Edge; //邻接矩阵 }; template<class T ,class E> Graphmtx<T,E>::Graphmtx(int sz) //构造函数 { maxVertices = sz; numVertices = 0; numEdges = 0; int i,j; VerticesList = new T[maxVertices]; Edge = new E *[maxVertices]; for (i=0;i<maxVertices;i++) Edge[i]= new E[maxVertices]; for(i=0;i<maxVertices;i++) for(j=0;j<maxVertices;j++) Edge[i][j] = (i==j) ? 0 :maxWeight; } template<class T,class E> int Graphmtx<T,E>::getFirstNeighbor(int v)//返回v的第一个邻接顶点的位置 { if(v!=-1) { for(int col =0;col<maxVertices;col++) if(Edge[v][col]>0 && Edge[v][col]<maxWeight) return col; } return -1; } template<class T,class E> int Graphmtx<T,E>::getNextNeighbor(int v,int w)//返回v的邻接顶点w的下一个邻接顶点 { if (v!=-1 && w!=-1) { for (int col =w+1;col<maxVertices;col++) { if(Edge[v][col]>0 &&Edge[v][col]<maxWeight) return col; } } return -1; } template<class T ,class E> bool Graphmtx<T,E>::insertVertex(const T& vertex) //插入一个顶点 { if(numVertices == maxVertices)return false; //顶点表已满,返回false VerticesList[numVertices++]=vertex; return true; } template<class T ,class E> bool Graphmtx<T,E>::insertEdge(int v1, int v2,E cost)//插入一条边 { if(v1>-1 && v1<numVertices && v2>-1 && v2<numVertices) //检查条件 { if( Edge[v1][v2]==maxWeight) { Edge[v1][v2]=Edge[v2][v1] = cost; numEdges++; return true; } else { cout<<"该边已存在,添加失败"<<endl; return false; } } else return false; } template<class T,class E> bool Graphmtx<T,E>::removeVertex(int v) //删除一个顶点 { if(v<0 ||v>numVertices) return false; //v不在图中 if(numVertices==1) return false; //只剩一个顶点,不删除 int i,j; VerticesList[v]=VerticesList[numVertices-1]; //顶点表中删除 for( i=0;i<numVertices;i++) //边数调整 if(Edge[i][v]>0 && Edge[i][v]<maxWeight) numEdges--; for(i=0;i<numVertices;i++) Edge[i][v]=Edge[i][numVertices-1]; numVertices--; //顶点数调整 for(j=0;j<numVertices;j--) Edge[v][j]=Edge[numVertices][j]; return true; } template<class T,class E> bool Graphmtx<T,E>::removeEdge(int v1,int v2) //删除边 { if(v1>-1 && v1<numVertices && v2>-1 &&v2<numVertices && Edge[v1][v2]>0 && Edge[v1][v2]<maxWeight) { Edge[v1][v2] = Edge[v1][v2] = maxWeight; numEdges--; return true; } else return false; }; template<class T ,class E> void Graphmtx<T,E>::inputGraph() { //通过从输入流对象in输入n的顶点和e条五项边的信息建立邻接矩阵表示的图G。邻接矩阵初始化工作在构造函数完成 int i,j,k,m,n; T e1,e2; E weight; cout<<"请输入顶点数和边数(空格隔开):"<<endl; cin >> n >> m; //输入点数n的边数m cout<<"请依次输入顶点:"<<endl; for(i=0;i<n;i++)//输入顶点,建立顶点表 { cin>>e1; this->insertVertex(e1); //G.insertVertex(e1); } cout<<"请依次输入边,形如 v1 v2 weight :"<<endl; i=0; while(i<m) { cin >> e1>>e2>>weight; j = this->getVertexPos(e1);//查顶点号 k = this->getVertexPos(e2); if(j==-1 || k==-1) { cout<<"边两端点信息有误,重新输入!"<<endl; } else { if(this->insertEdge(j,k,weight)) i++; } } } template<class T ,class E> void Graphmtx<T,E>::outputGraph() { //输出图的所有顶点和边信息 int i,j,n,m; T e1,e2; E weight; n = this->NumberOfVertices(); //点数 m = this->NumberOfEdges(); //边数 cout<<"顶点数的边数为:"; cout<<n<<","<<m<<endl; //输出点数和边数 cout<<"各边依次为:"<<endl; for(i=0;i<n;i++) { for(j=i+1;j<n;j++) { weight = this->getWeight(i,j); if(weight>0 && weight< maxWeight) { e1 = this->getValue(i); e2 = this->getValue(j); cout<<"("<<e1<<","<<e2<<","<<weight<<")"<<endl; } } } }
- 编写测试程序加以简单测试:
//Filename: test.cpp #include "Graphmtx.h" #include "Graphlnk.h" void test_Graphmtx() { char ch1,ch2; int weight; Graphmtx<char,int> g(30); g.inputGraph(); //构造图 g.outputGraph(); //显示图 cout<<"顶点数和边数:"<<g.NumberOfVertices()<<" "<<g.NumberOfEdges()<<endl; cout<<"查看第一个邻接顶点:"; cin>>ch1; cout<<g.getValue(g.getFirstNeighbor(g.getVertexPos(ch1)))<<endl; cout<<"查看后一个邻接顶点:"; cin>>ch1>>ch2; cout<<g.getValue(g.getNextNeighbor(g.getVertexPos(ch1),g.getVertexPos(ch2)))<<endl; cout<<"插入顶点:"; cin >>ch1; g.insertVertex(ch1); //插入点 cout<<"插入边:"<<endl; cin >>ch1>>ch2>>weight; g.insertEdge(g.getVertexPos(ch1),g.getVertexPos(ch2),weight);//插入边 g.outputGraph(); cout<<"删除边:"; cin >>ch1>>ch2; g.removeEdge(g.getVertexPos(ch1),g.getVertexPos(ch2)); //删除边 cout<<"删除点:"; cin>>ch1; g.removeVertex(g.getVertexPos(ch1)); g.outputGraph(); } void test_Graphlnk() { cout<<"-----------Grapglnk Test-----------"<<endl; char ch1,ch2; int weight; Graphmtx<char,int> g(30); g.inputGraph(); //构造图 g.outputGraph(); //显示图 cout<<"顶点数和边数:"<<g.NumberOfVertices()<<" "<<g.NumberOfEdges()<<endl; cout<<"查看第一个邻接顶点:"; cin>>ch1; cout<<g.getValue(g.getFirstNeighbor(g.getVertexPos(ch1)))<<endl; cout<<"查看后一个邻接顶点:"; cin>>ch1>>ch2; cout<<g.getValue(g.getNextNeighbor(g.getVertexPos(ch1),g.getVertexPos(ch2)))<<endl; cout<<"插入顶点:"; cin >>ch1; g.insertVertex(ch1); //插入点 cout<<"插入边:"<<endl; cin >>ch1>>ch2>>weight; g.insertEdge(g.getVertexPos(ch1),g.getVertexPos(ch2),weight);//插入边 g.outputGraph(); cout<<"删除边:"; cin >>ch1>>ch2; g.removeEdge(g.getVertexPos(ch1),g.getVertexPos(ch2)); //删除边 cout<<"删除点:"; cin>>ch1; g.removeVertex(g.getVertexPos(ch1)); g.outputGraph(); }
//Filename : main.cpp #include "Graphmtx.h" extern void test_Graphmtx(); extern void test_Graphlnk(); int main() { test_Graphmtx(); test_Graphlnk(); system("pause"); return 0; }
经测试,以上代码可以正确运行。