图的遍历

图是一种比线性表和树稍微复杂的数据结构,相比线性表的前驱后继和树的层次关系,图中任意两个元素之间都有可能存在关系。

图由非空的顶点集合和一个描述顶点之间关系的集合组成,记为 G = (V, E)。可分为 无向图 和 有向图。n个顶点的无向图中,如果任意两个顶点之间有且只有一条边,总的边数为 n(n-1)/2,这样的图称为完全图;n个顶点的有向图中,如果任意两个顶点之间有且只有一条边,总的边数为 n(n-1),这样的图称为有向完全图

顶点的度:顶点 v 的度是与该顶点 v 相关联的边的条数。对于有向图,顶点的度分为入度和出度,入度 是以v 为终点的边的条数 ,出度是以v为起点的边的条数。

顶点的度 =入度+出度。

权值:图的边可能附有权值,表示一个顶点到另一个顶点之间的距离时间等实际意义的量。带权的图又称为网。

简单路径:如果从一个顶点到另一个顶点,路径上所有顶点均不相同,则称该路径为简单路径。

子图:对于图G1 = (V1, E1)和图 G2 = (V2, E2),如果V2是V1的子集 且 E2是E1 的子集,则称G2是G1的子图。

连通图和连通分量: 无向图中,若存在顶点vi 到vj 的路径,则称顶点vi和vj是连通的,如果图中任意一对顶点都是连通的,则称该图是连通图。非连通图的最大连通子图称作连通分量。

强连通图和强连通分量:有向图中,如果每一对顶点vi和vj之间 从 vi 到 vj 和从vj 到 vi 都存在路径,则称之为强连通图;最大连通子图称为强连通分量。


图的存储结构:存储图的顶点信息和边的信息。

每个顶点都有可能与其他顶点存在联系,所有n个顶点的图 边的关系实质上用一个n*n的矩阵就足以描述。

两种最基本的图的表示方法有邻接矩阵和邻接链表表示法。

在稀疏图中,边的条数远小于顶点个数的平方,所以用邻接链表表示比较紧凑,若用邻接矩阵表示会浪费大量空间。

在稠密图中,边的条数接近顶点个数的平方,倾向于用邻接矩阵表示,因为可以迅速判断任意两个顶点之间是否有边相连。

邻接链表表示法的一个潜在缺陷是无法快速判断一条边(u,v)是否在图中存在,只能在以u开始的邻接链表中依次搜索节点v。

而邻接矩阵可以直接判断,但是又存储空间消耗。(无向图的邻接矩阵是对称矩阵,因而可以节省一半空间)


图的遍历:图的遍历是指从指定的某个顶点出发,按照一定的搜索方法对图中的所有顶点做一次访问的过程。

图的遍历过程中,可能存在回溯,所以利用一个数组标记每个顶点是否被访问过。

图的深度优先遍历(DFS):首先访问初始顶点vi,并将其标记为已被访问过,然后依次搜索 vi 的每一个邻接顶点 vj ,如果vj 没有被访问过,则以vj 为新的出发点继续深度优先遍历。依次类推,直到访问完所有顶点。

图的广度优先遍历(BFS):首先访问初始顶点 v ,并标记已被访问过,再访问初始顶点 v 的所有邻接节点vj ,并标记已被访问过;再依次访问vj 的邻接邻接顶点,依次类推,直到所有顶点被访问完。

下面举例说明,下图为无向图和其邻接链表存储。

事实上,根据邻接链表可以画出无向图,但是根据无向图却无法确定邻接链表,例如1的第一个指向可能是3 可能是2,这在遍历上有差别。

下面 分别从顶点1开始进行

DFS 遍历:

先访问顶点1(标记被访问,下略) ,再查找1的第一个未被访问邻接顶点3 <在以1开头的链表中查找>

访问顶点3,在以3开头的邻接链表查找第一个未被访问顶点4

访问顶点4,在以4开头的邻接链表查找第一个未被访问顶点7

访问顶点7,在以7开头的邻接链表查找第一个未被访问顶点8

访问顶点8,在以8开头的邻接链表查找第一个未被访问顶点6(7已被访问过)

访问顶点6,在以6开头的邻接链表查找第一个未被访问顶点(8,4都已被访问),查找失败,回溯到上一个顶点8

在以8开头的邻接链表查找第一个未被访问顶点 5(7 6 已被访问)

访问顶点5,在以5开头的邻接链表查找第一个未被访问顶点,查找失败,回溯到上一个顶点6 ,(依次回溯)查找失败,回溯到上一个顶点8;查找失败,回溯到上一个顶点        7;查找失败,回溯到上一个顶点4,查找到第一个未被访问顶点2.

访问顶点2,所有顶点都被访问过了,结束。

DFS-------> 1->3->4->7->8->6->5->2

BFS遍历:

先访问顶点1(标记被访问,下略),再访问1的所有邻接顶点

访问顶点3

访问顶点2


访问3的所有邻接顶点 4 (1已被访问) 

访问2的所有邻接顶点:无(4,1均已被访问)


访问4的所有邻接顶点:7 6 5 (3,2均已被访问)


访问7的所有邻接顶点:8 (4已被访问)

所有顶点都被访问完结束。

BFS-------->1->3->2->4->7->6->5->8

下面直接贴代码:

#define maximum 10

typedef struct Node
{
	int data;//邻接顶点
	struct Node *next;
} GraphNode;

GraphNode Graph[maximum]; //顶点数组
int rear = -1;
int front = -1;
//入队
void AddQueue (int *h, int x)
{
	if(rear == maximum-1)
	{
		printf("队列已满\n");
		return;
	}
	rear++;
	h[rear]=x;
	return;
}
//出队
int DelQueue(int *h)
{
	int e;//e=出队顶点值
	if(rear==front)
	{
		printf("Queue is empty!\n");
		return -1;
	}
	front++;//front指向队列队头的前一个元素
	e = h[front];
	h[front] = 0;
	return e;
}

//建立邻接表
void CreateAdjacentTable(int v1,int v2)
{
	GraphNode *newNode,*p;
	newNode = (GraphNode *)malloc(sizeof(GraphNode));
	newNode->data = v2;
	newNode->next = NULL;
	p = &Graph[v1];//p指向第v1个顶点
	while(p->next!=NULL)
		p=p->next;
	p->next = newNode;//新结点链接在v1最后
}

//DFS
void DFS(int *visited, int v)
{
	GraphNode *p;
	printf("%d->",v);
	visited[v] = 1;//已访问顶点
	p = Graph[v].next; //指向第v个顶点的第一个邻接顶点
	while(p != NULL)
	{
		if(visited[p->data] == 0)//如果存在且没有被访问过
			DFS(visited,p->data);//递归调用
		p = p->next;
	}
}

//BFS
void BFS(int *visited, int v, int *Queue)
{
	GraphNode *p;
	AddQueue(Queue,v);//第一个顶点入队列
	printf("%d->",v);
	visited[v] = 1;//已被访问
	while(front != rear)
	{
		v = DelQueue(Queue);//取队头
		p = Graph[v].next;
		while(p != NULL)
		{
			if(visited[p->data] ==0)
			{
				AddQueue(Queue,p->data);
				visited[p->data] = 1;
				printf("%d->",p->data);
			}
			p = p->next;
		}
	}
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值