前面给出了图的邻接矩阵的实现,现在给出图的邻接表的实现。这里仍然采用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语言版)》