【数据结构】 图的邻接表

邻接表是图的另一种存储结构(使用链表的思想)。

该方式的基本思路:顶点表后指向邻接表,邻接表中依次为当前顶点的邻接点

准备工作

与邻接矩阵类似,在构造邻接表之前,需要存储各个顶点的信息,在这里使用结构体存储。因此,在构造邻接表之前,需要先定义两个结构体:邻接表结构体、顶点结构体。【其中,都需要用到指向邻接表的指针!】

struct EdgeNode{		//邻接表 
	int ad;				//记录邻接点的位置 
	EdgeNode *next;		//指向邻接表的指针
};
struct VertxNode{		//顶点表 
	char vertx;
	EdgeNode *firstEdge;//指向邻接表的指针
};

类的声明

与邻接矩阵类似,在这里,继续使用C++面向对象的方法完成构建。

class AlGraph{
public:
	AlGraph(char a[],int n,int e);	//通过顶点数据、点数、边数建邻接表 
	~AlGraph();						//使用链表存储,非静态存储,需析构!
	void dfs(int v);				
	void bfs(int v);				
private:
	VertxNode adjlist[MaxSize];			//结构数组存放各顶点 
	int vertxNum,edgeNum;				//点数、边数 
	
}; 

建表

构造函数

构造函数与单链表的头插法类似,但又不完全相同。如图,单链表中的first结点为一个结构体变量,其中包含next指针。

在实现单链表的头插法时,首先将插入元素的next指针与first指针指向同一位置,之后将头指针的next指针指向新插入的元素。而在邻接表构建中,顶点结构体就相当于单链表头插构造中的整个first结点,顶点结构体中的firstEdge指针就相当于单链表first结点的next指针!(如图所示)

【图】

邻接表的构造过程用以下代码实现:

AlGraph::AlGraph(char a[],int n,int e){
	vertxNum=n,edgeNum=e;
	int i,j,k;
	//顶点结构初始化(抄入顶点数据并将头指全部置空) 
	for(i=0;i<vertxNum;i++){
		adjlist[i].vertx=a[i];
		adjlist[i].firstEdge=NULL;
	} 
	//使用链表头插法,对输入的边关系挂链 
	EdgeNode *s=NULL;
	for(k=0;k<edgeNum;k++){
		cout << "输入边的两个顶点编号:";
		cin>>i>>j;			//输入顶点,i已存入顶点结构,j存入邻接点域。
		s=new EdgeNode;s->ad=j;			
		//将j挂链到i上 (头插)
		s->next=adjlist[i].firstEdge;
		adjlist[i].firstEdge=s;
	} 
} 

析构函数

由于邻接表使用链表存储数据,为动态存储,不同于邻接矩阵的数组存储结构。因此,需要手动写析构函数!

析构过程的思路及代码如下:

AlGraph::~AlGraph(){
	//使用链表存储邻接点,存储结构非静态,需循环删除指针
	EdgeNode *p,*q;		//工作指针p ,临时指针q(暂存被删除元素) 
	p=NULL,q=NULL;
	for(int i=0;i<vertxNum;i++){
		p=q=adjlist[i].firstEdge;
		while(p){
			p=p->next;
			delete q;	//删除临时指针指向的数据 
			q=p; 		//更新临时指针指向 
		}
	}
} 

图的遍历

与邻接矩阵相同,有两种方式对图进行遍历:DFS、BFS。其思路与邻接矩阵时实现的思路完全一样,所不同的仅仅是对链表的遍历过程。

DFS

void AlGraph::dfs(int v){
	cout<<adjlist[v].vertx; vis[v]=1;
	//遍历邻接表
	EdgeNode *p=NULL;
	p=adjlist[v].firstEdge; 	
    //工作指针初始化为邻接表第一个元素 
	int i; 
	while(p){
		i=p->ad;
		if(!vis[i]) dfs(i);	
		p=p->next;
	} 
}

BFS

void AlGraph::bfs(int v){
	//思路:用队列,将元素访问、标记、入队 
	int front,rear;
	front=rear=-1;
	int Q[MaxSize],j,t;		//t存储出队元素,j存储邻接点位置 
	cout<<adjlist[v].vertx;
	vis[v]=1;
	Q[++rear]=v;
	
	//从起点出发遍历邻接表 
	EdgeNode *p=NULL;	
	while(front!=rear){
		t=Q[++front];
		p=adjlist[t].firstEdge;		//工作指针指向顶点后第一个邻接点
		
		while(p){
			j=p->ad;
		if(!vis[j]){
			cout<<adjlist[j].vertx;
			vis[j]=1;
			Q[++rear]=j;
		}
			p=p->next;
		}	
	} 
}

完整代码

​ 如图,测试边为:(0 1)(0 3)(0 4)(1 2)(2 4)(3 2)(3 4)

【图】

#include<iostream>
using namespace std;
const int MaxSize=10;
int vis[MaxSize]={0};
struct EdgeNode{		//邻接表 
	int ad;				//记录邻接点位置 
	EdgeNode *next;
};
struct VertxNode{		//顶点表 
	char vertx;
	EdgeNode *firstEdge;//顶点表指向邻接表,指针为邻接表类型 
};
//思路:顶点表后指向邻接表,邻接表中依次为当前顶点的邻接点 
class AlGraph{
public:
	AlGraph(char a[],int n,int e);	//通过顶点数据、点数、边数建邻接表 
	~AlGraph();						//使用链表存储,非静态存储,需析构!
	void dfs(int v);				
	void bfs(int v);				
private:
	VertxNode adjlist[MaxSize];			//结构数组存放各顶点 
	int vertxNum,edgeNum;				//点数、边数 
	
}; 
AlGraph::AlGraph(char a[],int n,int e){
	vertxNum=n,edgeNum=e;
	int i,j,k;
	//顶点结构初始化(抄入顶点数据并将头指全部置空) 
	for(i=0;i<vertxNum;i++){
		adjlist[i].vertx=a[i];
		adjlist[i].firstEdge=NULL;
	} 
	//使用链表头插法,对输入的边关系挂链 
	EdgeNode *s=NULL;
	for(k=0;k<edgeNum;k++){
		cout << "输入边的两个顶点编号:";
		cin>>i>>j;			//输入顶点,i已存入顶点结构,j存入邻接点域。
		s=new EdgeNode;s->ad=j;			
		//将j挂链到i上 (头插)
		s->next=adjlist[i].firstEdge;
		adjlist[i].firstEdge=s;
	} 
} 

void AlGraph::dfs(int v){
	cout<<adjlist[v].vertx; vis[v]=1;
	//遍历邻接表
	EdgeNode *p=NULL;
	p=adjlist[v].firstEdge; 	//工作指针初始化为邻接表第一个元素 
	int i; 
	while(p){
		i=p->ad;
		if(!vis[i]) dfs(i);	
		p=p->next;
	} 
}
			
void AlGraph::bfs(int v){
	//思路:用队列,将元素访问、标记、入队 
	int front,rear;
	front=rear=-1;
	int Q[MaxSize],j,t;		//t存储出队元素,j存储邻接点位置 
	cout<<adjlist[v].vertx;
	vis[v]=1;
	Q[++rear]=v;
	
	//从起点出发遍历邻接表 
	EdgeNode *p=NULL;	
	while(front!=rear){
		t=Q[++front];
		p=adjlist[t].firstEdge;		//工作指针指向顶点后第一个邻接点
		
		while(p){
			j=p->ad;
		if(!vis[j]){
			cout<<adjlist[j].vertx;
			vis[j]=1;
			Q[++rear]=j;
		}
			p=p->next;
		}	
	} 
}

AlGraph::~AlGraph(){
	//使用链表存储邻接点,存储结构非静态,需循环删除指针
	EdgeNode *p,*q;		//工作指针p ,临时指针q(暂存被删除元素) 
	p=NULL,q=NULL;
	for(int i=0;i<vertxNum;i++){
		p=q=adjlist[i].firstEdge;
		while(p){
			p=p->next;
			delete q;	//删除临时指针指向的数据 
			q=p; 		//更新临时指针指向 
		}
	}
} 

int main( )
{

	char ch[ ] = {'A','B','C','D','E'};
	int i;
	AlGraph ALG(ch, 5, 7);               //建立具有5个顶点6条边的有向图
	
	for (i = 0; i < MaxSize; i++)
		vis[i] = 0;
	cout << "深度优先遍历序列是:";
	ALG.dfs(0);                       //从顶点0出发进行深度优先遍历
	cout<<endl;
	
	for (i = 0; i < MaxSize; i++)
		vis[i] = 0;
	cout << "广度优先遍历序列是:"; 
	ALG.bfs(0);                      //从顶点0出发进行广度优先遍历
	return 0;
}

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值