图的邻接表实现

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值