图的邻接表实现

来源:互联网 发布:上海知楚仪器 编辑:程序博客网 时间:2024/05/21 14:45

前面给出了图的邻接矩阵的实现,现在给出图的邻接表的实现。这里仍然采用vector来存储顶点表,顶点的插入比较简单,顶点和边的删除以及边的插入,实质上就是一系列的简单链表操作,下面直接给出代码,代码中有详细的注释。

代码以有向带权图为例,对于无向图,非权图,代码只需作简单修改。

1、Graph的实现

#ifndef _GRAPH_H_#define _GRAPH_H_#include<vector>#include<string>#include<fstream>using namespace std;//边结点结构 //template<class T>struct Edge{int verAdj;//邻接顶点序号 int weight;//边的权值 Edge* next;//指向下一个边结点的指针 Edge(int pos, int w, Edge* p=NULL): verAdj(pos),weight(w),next(p){}};//顶点表中结点的结构 template<class T>struct Vertex{T verName;//顶点名称Edge* adjacent;//边链表的头指针 Vertex(){}Vertex(const T& v, Edge* p=NULL): verName(v),adjacent(p){}};//图的类定义 template<class T>class Graph{private:vector< Vertex<T> > vertexList;//用向量存储顶点int getVertexPosition(const T& v)const;bool findVertex(const T& v)const;public: Graph(){}Graph(string fileName);~Graph();bool isEmpty()const;void printGraph()const;int getNumberOfVertex()const;T getVertexName(const int pos)const;int getWeight(const T& v1,const T& v2)const;int getFirstNeighbor(const int v)const;int getNextNeighbor(const int v1,const int v2)const;Edge* getNeighbors(const T& v)const;void insertVertex(const T& v);void deleteVertex(const T& v);void insertEdge(const T& from, const T& to, int weight);void deleteEdge(const T& from, const T& to);void readGraph(string fileName);};//构造函数,参数给出了构造图的文件路径 template <class T>Graph<T>:: Graph(string fileName){readGraph(fileName);}//析构函数,删除图 template <class T>Graph<T>:: ~Graph(){Edge* q = NULL;//删除每个顶点的边链表 for(int i=0; i<vertexList.size(); i++){Edge* p = vertexList[i].adjacent;vertexList[i].adjacent = NULL;while(p != NULL){q = p;p = p->next;delete q;}}}//以邻接表的形式输出图 template <class T>void Graph<T>:: printGraph()const{for(int i=0; i<vertexList.size(); i++){cout<<i<<":"<<vertexList[i].verName;Edge* p = vertexList[i].adjacent;while(p != NULL){cout<<"->("<<p->verAdj<<":"<<getVertexName(p->verAdj)<<",";cout<<p->weight;cout<<")";p = p->next;}cout<<endl;}cout<<endl<<endl;}template<class T>int Graph<T>:: getNumberOfVertex()const{return vertexList.size();}//返回顶点v在顶点表中的序号 template<class T>int Graph<T>:: getVertexPosition(const T& v)const{for(int i=0; i<vertexList.size(); i++){if(vertexList[i].verName == v){return i;}}return -1;//v不在顶点表中 }//检查顶点v否已在顶点表中template <typename T>bool Graph<T>:: findVertex(const T& v)const{if(getVertexPosition(v) != -1){return true;}return false;}/*返回序号为pos的顶点的名称  *可以不作范围检查,让用户自己保证 */template<class T>T Graph<T>:: getVertexName(const int pos)const{if(pos >= 0 && pos < vertexList.size())return vertexList[pos].verName;//else//throw Exception;}//判断图是否为空 template<class T>bool Graph<T>:: isEmpty()const{return vertexList.empty();}//求边的权值 template<class T>int Graph<T>:: getWeight(const T& v1,const T& v2)const{int pos1 = getVertexPosition(v1);int pos2 = getVertexPosition(v2);if(pos1 != -1 && pos2 != -1){Edge* p = vertexList[pos1].adjacent;//v1的边链表头指针while(p != NULL){if(p->verAdj == pos2){return p->weight;}p = p->next;}}return 0;//不存在此边,返回0}//求序号为v的顶点的第一个邻接顶点序号 template<class T>int Graph<T>:: getFirstNeighbor(const int v)const{if(v != -1){Edge* p = vertexList[v].adjacent;if(p != NULL){return p->verAdj;}}return -1;//不存在此顶点或边链表为空 }//求序号为v1的顶点相对于序号为v2的顶点的下一个邻接顶顶的序号 template<class T>int Graph<T>:: getNextNeighbor(const int v1,const int v2)const{if(v1 != -1 && v2 != -1){Edge* p = vertexList[v1].adjacent;while(p != NULL && p->verAdj != v2){p = p->next;}if(p != NULL && p->next != NULL){return p->next->verAdj;}}return -1;}//求顶点v的边链表 template<class T>Edge* Graph<T>:: getNeighbors(const T& v)const{int pos = getVertexPosition(v);if(pos != -1){return vertexList[pos].adjacent;}return NULL;//顶点v不在图中 }//向图中插入一个顶点v template<class T>void Graph<T>:: insertVertex(const T& v){if(findVertex(v))//图中已存在该顶点 return;Vertex<T> vertex(v);vertexList.push_back(vertex);}//从图中删除一个顶点v template<class T>void Graph<T>:: deleteVertex(const T& v){int pos = getVertexPosition(v);if(pos != -1){Edge* pre = NULL;Edge* q = NULL;/*Step 1: *删除以该顶点为始点的边,即删除边链表*/Edge* p = vertexList[pos].adjacent;vertexList[pos].adjacent = NULL;//边链表置空 while(p != NULL){q = p;p = p->next;delete q;}/*Step 2: *完整扫描边链表,删除以该顶点为终点的边, *并更新边结点顶点的序号  *注意某一顶点到顶点v的边最多只有一条, *最多只需删除1个结点 */for(int i=0; i<vertexList.size(); i++){p = vertexList[i].adjacent;//从头结点开始扫描 while(p != NULL){if(p->verAdj == pos)//是要删除的结点 {q = p;//q指向要删除的结点 if(p == vertexList[i].adjacent)//要删除的是头结点 {vertexList[i].adjacent = p->next;}else//非头结点 {pre->next = p->next;}p = p->next;delete q;}else//不是要删除的结点,更新顶点序号 {if(p->verAdj > pos)//序号大于pos的顶点向前移动,减1 p->verAdj--;pre = p;//指向p的前驱结点 p = p->next;}}}//Step 3:从顶点表中删除该顶点vertexList.erase(vertexList.begin() + pos);}}//向图中插入边,from为始边,to为终边 template<class T>void Graph<T>:: insertEdge(const T& from, const T& to, int weight){int pos1 = getVertexPosition(from);int pos2 = getVertexPosition(to);if(pos1 != -1 && pos2 != -1){//始终在表头插入边结点Edge * p = new Edge(pos2 , weight);p->next = vertexList[pos1].adjacent;vertexList[pos1].adjacent = p;}}//从图中删除边,from为始边,to为终边 template<class T>void Graph<T>:: deleteEdge(const T& from, const T& to){int pos1 = getVertexPosition(from);int pos2 = getVertexPosition(to);if(pos1 != -1 && pos2 != -1){Edge* p = vertexList[pos1].adjacent;Edge* q = NULL;Edge* pre = NULL; while(p != NULL){if(p->verAdj == pos2)//找到结点 {q = p;if(p == vertexList[pos1].adjacent)//删除的是头结点 {vertexList[pos1].adjacent = p->next;}else{pre->next = p->next;}delete q;break;//结束查找} else//继续查找 {pre = p;p = p->next;}}}}template<class T>void Graph<T>:: readGraph(string fileName){int vertexNumber;T vertex;T v1,v2;int weight;ifstream in(fileName.c_str(),ios_base::in);if(!in){cout<<"file open error!"<<endl;exit(1);}in>>vertexNumber;//读入顶点数 for(int i=0; i<vertexNumber; i++){in>>vertex;//读入顶点名称 insertVertex(vertex);//插入顶点}while(in>>v1>>v2>>weight){insertEdge(v1, v2, weight);//插入边 }}#endif

2、简单的测试程序

int main(int argc, char *argv[]){Graph<char> graph("graph1.dat");cout<<"after read the graph :"<<endl;graph.printGraph();cout<<"after delete edge "<<"<A,C>,<C,A>"<<endl;graph.deleteEdge('A','C');graph.deleteEdge('C','A');graph.printGraph();//cout<<"after delete vertex "<<"D :"<<endl;//graph.deleteVertex('D');//graph.printGraph();cout<<"after delete vertex "<<"B :"<<endl;graph.deleteVertex('B');graph.printGraph();    system("PAUSE");    return EXIT_SUCCESS;}

3、graph1.dat内容

4

A B C D

A B 3

A C 7

A D 5

B A 6

B C 9

B D 2

C A 6

D C 10

D B 4

4、程序输出

after read the graph :

0:A->(3:D,5)->(2:C,7)->(1:B,3)

1:B->(3:D,2)->(2:C,9)->(0:A,6)

2:C->(0:A,6)

3:D->(1:B,4)->(2:C,10)

 

after delete edge <A,C>,<C,A>

0:A->(3:D,5)->(1:B,3)

1:B->(3:D,2)->(2:C,9)->(0:A,6)

2:C

3:D->(1:B,4)->(2:C,10)

 

after delete vertex B :

0:A->(2:D,5)

1:C

2:D->(1:C,10)

 

请按任意键继续. ..


参考资料:

[1]《数据结构》刘大有 唐海鹰等著 高等教育出版社

[2][严蔚敏《数据结构(C语言版)》


原创粉丝点击