图的存储方法:邻接矩阵、邻接表

例如:有一个图如下所示(该图也作为程序的实例):

则上图用邻接矩阵可以表示为:

邻接表可以表示如下:

邻接矩阵可以很容易的用二维数组表示,下面主要看看怎样构成邻接表

邻接表存储方法是一种顺序存储与链式存储相结合的存储方法。在这种方法中,只考虑非零元素,所以在图中的顶点很多而边很少时,可以节省存储空间

        邻接表存储结构由两部分组成:对于每个顶点vi, 使用一个具有两个域的结构体数组来存储,这个数组称为顶点表。其中一个域称为顶点域(vertex),用来存放顶点本身的数据信息;而另一个域称为指针域(link),用来存放依附于该顶点的边所组成的单链表的表头结点的存储位置。邻接于vi的顶点vj链接成的单链表称为vi的邻接链表。邻接链表中的每个结点由两个域构成:一是邻接点域(adjvex),用来存放与vi相邻接的顶点vj的序号j (可以是顶点vj在顶点表中所占数组单元的下标);
其二是链域(next),用来将邻接链表中的结点链接在一起。具体的程序实现如下:

void CreateAdjTable(vexnode ga[N],int e)//创建邻接表
{
	int i,j,k;
	edgenode *s;
	printf("\n输入顶点的内容:");
	for(i=0;i<N;i++)
	{
		ga[i].vertex=getchar();//读入顶点的内容

		ga[i].link=NULL;//初始化
	}
	printf("\n");
	for(k=0;k<e;k++)
	{
		printf("输入边的两个顶点的序号:");
		scanf("%d%d",&i,&j);//读入边的两个顶点的序号
		s=(edgenode *)malloc(sizeof(edgenode));
		s->adjvex=j;
		s->next=ga[i].link;
		ga[i].link=s;

		s=(edgenode *)malloc(sizeof(edgenode));
		s->adjvex=i;
		s->next=ga[j].link;
		ga[j].link=s;

	}
}

广度优先搜索遍历(BFS):

        图的广度优先搜索遍历类似于树的按层次遍历。在假设初始状态是图中所有顶点都未被访问的条件下,这种方法从图中某一顶点vi出发,先访问vi,然后访问vi的邻接点vj。在所有的vj都被访问之后,再访问vj的邻接点vk,依次类推,直到图中所有和初始出发点vi有路径相通的顶点都被访问为止。若图是非连通的,则选择一个未曾被访问的顶点作为起始点,重复以上过程,直到图中所有顶点都被访问为止。 

        在这种方法的遍历过程中,先被访问的顶点,其邻接点也先被访问,具有先进先出的特性,所以可以使用一个队列来保存已访问过的顶点,以确定对访问过的顶点的邻接点的访问次序。为了避免重复访问一个顶点,也使用了一个辅助数组visited[n]来标记顶点的访问情况。下面分别给出以邻接矩阵和邻接表为存储结构时的广度优先搜索遍历算法BFS_matrix和BFS_AdjTable:

具体程序实现如下:

#include<stdio.h>
#include<stdlib.h>
#define N 5

//邻接矩阵存储法
typedef struct
{
	char vexs[N];//顶点数组
	int arcs[N][N];
}graph;

//邻接表存储法
typedef struct Node
{
	int adjvex;
	struct Node *next;

}edgenode;

typedef struct
{
	char vertex;
	edgenode *link;
}vexnode;

//队列操作
typedef struct node
{
	int data;
	struct node *next;
}linklist;

typedef struct
{
	linklist *front,*rear;
}linkqueue;

void SetNull(linkqueue *q)//队列置空
{
	q->front=(linklist *)malloc(sizeof(linklist));
	q->front->next=NULL;
	q->rear=q->front;
}

int Empty(linkqueue *q)
{
	if(q->front==q->rear)
		return 1;
	else
		return 0;
}

int Front(linkqueue *q)//取队头元素
{
	if(Empty(q))
	{
		printf("queue is empty!");
		return -1;
	}
	else
		return q->front->next->data;
}

void ENqueue(linkqueue *q,int x)//入队
{
	linklist * newnode=(linklist *)malloc(sizeof(linklist));
    q->rear->next=newnode;
	q->rear=newnode;
	q->rear->data=x;
	q->rear->next=NULL;

}

int DEqueue(linkqueue *q)//出队
{
	int temp;
	linklist *s;
	if(Empty(q))
	{
		printf("queue is empty!");
		return -1;
	}
	else
	{
		s=q->front->next;
		if(s->next==NULL)
		{
			q->front->next=NULL;
			q->rear=q->front;
		}
		else
			q->front->next=s->next;
		temp=s->data;
		return temp;
	}
}

void BFS_matrix(graph g,int k,int visited[N])//图按照邻接矩阵存储时的广度优先遍历
{
	int i=0;
	linkqueue q;
	SetNull(&q);
	printf("%c\n",g.vexs[k]);
	visited[k]=1;
	ENqueue(&q,k);
	while(!Empty(&q))
	{
		i=DEqueue(&q);
		for(int j=0;j<N;j++)
		{
			if(g.arcs[i][j]==1&&visited[j]!=1)
			{
				printf("%c\n",g.vexs[j]);
				visited[j]=1;
				ENqueue(&q,j);
			}
		}
	}
}

void CreateAdjTable(vexnode ga[N],int e)//创建邻接表
{
	int i,j,k;
	edgenode *s;
	printf("\n输入顶点的内容:");
	for(i=0;i<N;i++)
	{
		ga[i].vertex=getchar();//读入顶点的内容

		ga[i].link=NULL;//初始化
	}
	printf("\n");
	for(k=0;k<e;k++)
	{
		printf("输入边的两个顶点的序号:");
		scanf("%d%d",&i,&j);//读入边的两个顶点的序号
		s=(edgenode *)malloc(sizeof(edgenode));
		s->adjvex=j;
		s->next=ga[i].link;
		ga[i].link=s;

		s=(edgenode *)malloc(sizeof(edgenode));
		s->adjvex=i;
		s->next=ga[j].link;
		ga[j].link=s;

	}
}

void BFS_AdjTable(vexnode ga[],int k,int visited[N])//图按照邻接表存储时的广度优先遍历
{
	int i=0;
	edgenode *p;
	linkqueue q;
	SetNull(&q);
	printf("%c\n",ga[k].vertex);
	visited[k]=1;//标记是否被访问过
	ENqueue(&q,k);//入队
	while(!Empty(&q))
	{
		i=DEqueue(&q);
		p=ga[i].link;
		while(p!=NULL)
		{
			if(visited[p->adjvex]!=1)
			{
				printf("%c\n",ga[p->adjvex].vertex);
				visited[p->adjvex]=1;
				ENqueue(&q,p->adjvex);
			}
			p=p->next;
		}
	}
}

void main()
{
	graph g;
	vexnode ga[N];
	int visited[5]={0};
	int visited1[5]={0};
	g.vexs[0]='A';
	g.vexs[1]='B';
	g.vexs[2]='C';
	g.vexs[3]='D';
	g.vexs[4]='E';
	int a[5][5]={{0,1,0,1,1},{ 1,0,1,0,1},{ 0,1,0,0,0},{ 1,0,0,0,0},{ 1,1,0,0,0}};
	for(int i=0;i<5;i++)
		for(int j=0;j<5;j++)
			g.arcs[i][j]=a[i][j];
	printf("图按照邻接矩阵存储时的广度优先遍历:\n");
	BFS_matrix(g,0,visited);
	CreateAdjTable(ga,5);
	printf("图按照邻接表存储时的广度优先遍历:\n");
	BFS_AdjTable(ga,0,visited1);
}

其结果如下图:



从上面可以看出,两种方式的结果不同,但都是正确的,因为这与邻接点访问的顺序有关。

注:如果程序出错,可能是使用的开发平台版本不同,请点击如下链接: 解释说明

原文:http://blog.csdn.net/tengweitw/article/details/17228937

作者:nineheadedbird

【算法导论】图的广度优先搜索遍历(BFS)的更多相关文章

  1. 图的广度优先搜索(BFS)

    把以前写过的图的广度优先搜索分享给大家(C语言版) #include<stdio.h> #include<stdlib.h> #define MAX_VERTEX_NUM 20 ...

  2. 基于visual Studio2013解决算法导论之046广度优先搜索

     题目 广度优先搜索 解决代码及点评 // 图的邻接表表示.cpp : 定义控制台应用程序的入口点. // #include <iostream> #include <stac ...

  3. 图的广度优先/层次 遍历(BFS) c++ 队列实现

    在之前的博文中,介绍了图的深度优先遍历,并分别进行了递归和非递归实现.BFS 无法递归实现,最广泛的实现是利用队列(queue).这与DFS的栈实现是极其相似的,甚至代码几乎都很少需要改动.从给定的起 ...

  4. 【算法导论】图的深度优先搜索遍历(DFS)

    关于图的存储在上一篇文章中已经讲述,在这里不在赘述.下面我们介绍图的深度优先搜索遍历(DFS). 深度优先搜索遍历实在访问了顶点vi后,访问vi的一个邻接点vj:访问vj之后,又访问vj的一个邻接点, ...

  5. Leetcode之广度优先搜索(BFS)专题-133. 克隆图(Clone Graph)

    Leetcode之广度优先搜索(BFS)专题-133. 克隆图(Clone Graph) BFS入门详解:Leetcode之广度优先搜索(BFS)专题-429. N叉树的层序遍历(N-ary Tree ...

  6. 数据结构之 图论---基于邻接矩阵的广度优先搜索遍历(输出bfs遍历序列)

    数据结构实验图论一:基于邻接矩阵的广度优先搜索遍历 Time Limit: 1000MS Memory limit: 65536K 题目描述 给定一个无向连通图,顶点编号从0到n-1,用广度优先搜索( ...

  7. Leetcode之广度优先搜索(BFS)专题-详解429. N叉树的层序遍历(N-ary Tree Level Order Traversal)

    Leetcode之广度优先搜索(BFS)专题-429. N叉树的层序遍历(N-ary Tree Level Order Traversal) 给定一个 N 叉树,返回其节点值的层序遍历. (即从左到右 ...

  8. C语言数据结构与算法之深度、广度优先搜索

    一.深度优先搜索(Depth-First-Search 简称:DFS) 1.1 遍历过程: (1)从图中某个顶点v出发,访问v. (2)找出刚才第一个被顶点访问的邻接点.访问该顶点.以这个顶点为新的顶 ...

  9. Z1. 广度优先搜索(BFS)解题思路

    /** BFS 解题思路 特点:从某些特定的节点开始,感染相邻的节点; 被感染的节点,再感染其相邻的节点,以此类推. 题目常见于数据结构包括 二维数组.树.图 **/ /** 1). 二维数组特定节点 ...

随机推荐

  1. Docker 网络

    Docker 的网络实现其实就是利用了 Linux 上的网络名字空间和虚拟网络设备(特别是 veth pair).建议先熟悉了解这两部分的基本概念再阅读本章. 基本原理 首先,要实现网络通信,机器需要 ...

  2. Latex:TexStudio的使用

    http://blog.csdn.net/pipisorry/article/details/54565608 Texsdudio 快捷键 The keyboard shortcuts can be ...

  3. Dynamics CRM2016 Web API之删除单个查找字段值

    之前的博文中有介绍过,Web Api中的一个删除单个属性的Api但没提供查找字段的删除方法,本篇补充上,这里给出的示例代码是C#的(主要看url的拼接),看下url中最后的/$ref,这个标示表明了当 ...

  4. 【Java集合系列】---总体框架

    个的组合,这些数据项可能共享某些特征,需要以某种操作方式一起进行操作,一般来说,这些数据项的类型都是相同的,或者基类相同(若使用的语言支持继承),列表或数组通常不认为是集合,因为其大小固定,但是事实上 ...

  5. TCP发送源码学习(1)--tcp_sendmsg

    一.tcp_sendmsg()函数分析: int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t ...

  6. linux 防火墙操作

    root/12345 (只能用ROOT操作)iptables -I INPUT -s x.x.x.x -p tcp --dport 8091 -j ACCEPT   #允许x.x.x.x访问本机的80 ...

  7. Linux 高性能服务器编程——多进程编程

    问题聚焦:     进程是Linux操作系统环境的基础.     本篇讨论以下几个内容,同时也是面试经常被问到的一些问题:     1 复制进程映像的fork系统调用和替换进程映像的exec系列系统调 ...

  8. springMVC源码分析--DispatcherServlet请求获取及处理

    在之前的博客springMVC源码分析--容器初始化(二)DispatcherServlet中我们介绍过DispatcherServlet,是在容器初始化过程中出现的,我们之前也说过Dispatche ...

  9. JBOSS EAP 6 系列三 Oracle、Mysql数据源的配置(驱动)—认识模块的使用

    本文介绍JBOSS EAP 6.2中Oracle数据源的配置方式.结合之前JBOSS EAP 6.2新功能,本文初识JBOSS模块申明式容器这一特性. 模块申明式容器:JBOSS EAP不再有lib的 ...

  10. Linux目录架构详解

    Linux和Windows操作系统的显著区别之一就是目录架构的不同.Linux操作系统的目录架构遵循文件系统层级结构标准.不知你是否使用ls命令浏览过Linux的根目录"/",亲爱 ...