【数据结构】 图的邻接表

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

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

准备工作

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

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;
}

这是我的作业。。。。希望对各位有#include <stdio.h> #include <malloc.h> #include <stdlib.h> #define SIZE (xsize*ysize+1) //一系列全局变量便于传递参数 int *location,*way, xsize,ysize,firstx,firsty, noworder; int getnum (void);//取数函数,取数成功返回1,否则返回-1 int init (void); //初始化函数,申请数组空间,以及初始化数组, //申请成功返回1,否则返回-1 int play (void); //下棋函数,下棋成功返回1,否则返回-1 int back (void); //悔棋函数,悔棋成功返回1,否则返回-1 void print (void);//输出函数,顺序输出马踩的棋盘一维坐标 //////////////////////////// void main () { int canget,caninit,canplay,canback; do canget=getnum(); while(canget==-1); caninit=init(); if(caninit==-1) exit (0);//终止程序运行 for (;noworder<SIZE-1;) { if(way[location[noworder]]>0 && way[location[noworder]]<=8) { canplay=play(); if(canplay==-1) way[location[noworder]]++;//当前方法不可行,改变方法 } else { canback=back(); if(canback==-1) { printf("不可能遍历整个棋盘!\n"); getchar();getchar(); exit (0);//当程序不能再悔棋时终止程序运行 } else way[location[noworder]]++; //当前方法不可行,改变方法 } } if(noworder==SIZE-1)//已经遍历整个棋盘 print(); getchar();getchar(); } //////////////////////////// int getnum() { printf("输入棋盘规格(假定无0点)和入口坐标:\n"); printf("输入棋盘规格xsize="); scanf("%d",&xsize); printf("输入棋盘规格ysize="); scanf("%d",&ysize); printf("输入入口坐标x="); scanf("%d",&firstx); printf("输入入口坐标y="); scanf("%d",&firsty); if (firstx>xsize || firsty>ysize || firstx<=0 || firsty<=0 || xsize <3 || ysize<3) { printf("输入有误,重新输入:\n\n\a"); return -1; } else return 1; } //////////////////////////// int init (void) { location=(int *)malloc(sizeof(int)*SIZE); way=(int *)malloc(sizeof(int)*SIZE); if(location==NULL || way==NULL) { printf("系统申请内存空间失败!程序执行终止!\a"); return -1; } for(int i=0;i<SIZE;i++)//初始化数组 { way[i]=0; location[i]=0; } noworder=1; location[1]=(firsty-1)*xsize+firstx; way[location[1]]=1; return 1; } //////////////////////////// void print(void) { printf("\n\n可以遍历,顺序如下:\n\n"); for (int i=1;i<SIZE;i++) { printf("%3d-->",location[i]); printf("OK\n"); } } //////////////////////////// int play() { int x,y,nextlocation; //一维坐标值à二维坐标值 x=location[noworder] % xsize; if(x==0) x=xsize; y=(location[noworder]-x)/xsize+1; switch (way[location[noworder]]) { case 1 : x+=2;y-=1;break; case 2 : x+=2;y+=1;break; case 3 : x+=1;y+=2;break; case 4 : x-=1;y+=2;break; case 5 : x-=2;y+=1;break; case 6 : x-=2;y-=1;break; case 7 : x-=1;y-=2;break; case 8 : x+=1;y-=2;break; } nextlocation = xsize*(y-1)+x; if (x>xsize || y>ysize || x<=0 || y<=0 || way[nextlocation]!=0)//越界或重复 return -1; else//下棋 { noworder++; location[noworder] = nextlocation; way[location[noworder]]=1; return 1; } } //////////////////////////// int back (void) { if(noworder==1)//不能再悔棋,不能遍历 return -1; else { way[location[noworder]]=0;//注意不能搞错语句顺序 location[noworder]=0; noworder--; return 1; } }用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值