图和图的遍历

由于本人没学过离散数学,在这里只能班门弄斧一下了:

一幅图(graph)G = (V,E)是由顶点集V(vertex)和边集E(egge)组成的。每一条边就是一个点对(v,w),其中v,w∈V。如果点对是有序的,那么图就是有向的,否则成为无向图。如果两个顶点(v1,v2)∈E,那么它们就是相关联的,它们互为邻接点。一个点的度数,指的是与这个点相关联的顶点的个数。对于有向图,还分为入度和出度。如果有一点序列(v1,v2,……vn),如果其中相邻的两个(vi,vi+1)∈E,则它们构成路径。如果v1=vn,则称为环或者回路。如果能从点v1沿着路径到v2,那就称这两点是联通的。如果图中的任意两点都是联通的,那么就称为连通图。如果有令一幅图G'=(V',E'),且V'∈V,E'∈E,则称G'是G的子图。特别的如果两幅图的顶点完全相同,只是第二幅图的边集是第一幅的子集,则第二幅图称为第一幅图的生成子图。

图的基本概念先说这么多吧,其实画出来以后这些概念都很明了的。

那么这种复杂的数据结构,即有点,又有边,该如何表示呢?人们想出了有很多种表示法:邻接矩阵,邻接表等等。这里只介绍最简单的邻接矩阵:就是把图中的n个顶点写成一个n*n的矩阵,如果两个顶点是关联的,那么矩阵的值为1,否者为0。

#include <stdio.h>
#include <malloc.h>
//深度优先搜索需要使用栈
#include"stack.h"
//广度优先遍历需要使用队列
#include "queue.h"
#define MAX 8

//邻接矩阵
typedef int** adjacentMatrix;

//图的数据结构
typedef struct graph
{
	//邻接矩阵
	adjacentMatrix matrix;
	//顶点向量
	char* vextex;
	//顶点向量个数
	int vextexNum;
	//边的个数
	int arcNum;
}Graph;


//图的基本操作
//初始化图
Graph initGraph(int );

//销毁图
void destroyGraph(Graph* );

//增加一条边
bool addArc(Graph* ,char ,char );

//删除一条边
bool deleteArc(Graph* ,char ,char );

//显示图
void showGraph(Graph* );


//图的遍历:
//深度优先搜索的递归实现
void do_DFS(Graph* ,int );
void DFS(Graph* );

//深度优先搜索的栈实现
int* findAdjacentVertex(Graph* ,int ,int *);
void DFS_byStack(Graph*,char);

//广度优先搜索的队列实现
void BFS_byQueue(Graph*,char);


 一种数据结构,最重要的操作就是对他遍历。对于图的遍历,有两种常用的方法:深度优先遍历和广度优先遍历。深度优先遍历类似于对二叉树的先序遍历;广度优先遍历类似按层遍历树。

广度优先遍历的基本步骤如下:

1.将初始节点入队

2.当队列不为空时,出队;出队的节点放在vx中。

3.找到与vx相邻且没有被访问过的节点,将它们入队。

4.转2.

注意在每次出队以后,就可以进行相应的操作了,比如打印。

深度优先遍历的基本步骤如下:

1.将起始节点压栈

2.如果栈不为空,弹栈;弹出的节点放在vx中。

3.找到与vx相邻且没被访问过的节点,将它们压栈。

4.转2.

 

下面给出相应的代码:

#include "graph.h"


//图的基本操作
//初始化图
Graph initGraph(int n)
{
	Graph g;
	g.vextexNum = n;
	g.arcNum = 0;
	g.matrix = (int**)malloc(sizeof(int*) * n);
	for(int i = 0; i < n;++i)
		g.matrix[i] = (int*)malloc(sizeof(int) * n);

	for(int i = 0;i < n;++i)
		for(int j = 0; j < n;++j)
			g.matrix[i][j] = 0;

	g.vextex = (char*)malloc(sizeof(char) * n);
	char a = 'A';
	for(int i = 0; i < n; ++i)
		g.vextex[i] = a+i;
	return g;
}

//销毁图
void destroyGraph(Graph* g)
{
	free(g->vextex);
	g->vextex = NULL;
	for(int i = 0; i < g->vextexNum;++i)
		free(g->matrix[i]);
	free(g->matrix);
	g->matrix = NULL;
	g->arcNum = -1;
	g->vextexNum = -1;
}

//增加一条边
bool addArc(Graph* g,char vex1,char vex2)
{
	int m = vex1-'A';
	int n = vex2-'A';	
	if(m < 0 || m > g->vextexNum || n < 0 || n > g->vextexNum )
	{
		printf("2 vertexes must in the graph\n");
		return false;
	}
	else
	{
		g->matrix[m][n] = 1;
		g->matrix[n][m] = 1;
		++g->arcNum;
		return true;
	}

}

//删除一条边
bool deleteArc(Graph* g,char vex1,char vex2)
{
	int m = vex1-'A';
	int n = vex2-'A';
	if(0 == g->matrix[m][n])
	{
		printf("this arc does not exsit!\n");
		return false;
	}
	else
	{
		g->matrix[m][n] = 0;
		g->matrix[n][m] = 0;
		g->arcNum--;
		return true;
	}

}

//显示图
void showGraph(Graph* g)
{
	printf("  ");
	for(int i = 0; i < g->vextexNum;++i)
		printf("%c ",g->vextex[i]);
	for(int i = 0; i < g->vextexNum;++i)
	{
		printf("\n");
		printf("%c ",g->vextex[i]);
		for(int j = 0;j < g->vextexNum;++j)
			printf("%d ",g->matrix[i][j]);
	}
	printf("\n");
}

//图的遍历:
//深度优先搜索的递归实现
//需要使用全局变量记录某个顶点是否被访问过
bool visited_DFS[MAX];
void DFS(Graph* g)
{
	printf("使用递归深度优先遍历:\n依次访问节点:");
	//访问开始前,所有节点均未被访问过
	for(int i = 0;i < g->vextexNum;++i)
		visited_DFS[i] = false;
	//对于每个节点,如果它未被访问,则调用深度优先搜索
	for(int i = 0; i < g->vextexNum;++i)
	{
		if(false == visited_DFS[i])
			do_DFS(g,i);
	}
	printf("\n");
}


void do_DFS(Graph* g,int i)
{
	visited_DFS[i] = true;
	printf("%c ",g->vextex[i]);
	//依次搜索i的邻接点
	for(int j = 0; j < g->vextexNum;++j)
	{
		//如果j是i的邻接点,且未被访问过,则对j调用深度优先搜索
		if(visited_DFS[j] == false && 1 == g->matrix[i][j])
		do_DFS(g,j);
	}

}


void DFS_byStack(Graph* g,char Start)
{
	int start = Start-'A';
	printf("使用队列进行深度优先遍历:\n依次访问节点:");
	//建立数组来标记节点是否被访问过
	bool *is_visited = (bool*)malloc(sizeof(bool) * g->vextexNum);
	for(int i = 0;i < g->vextexNum;++i)
		is_visited[i] = false;
	//建立一个栈
	Stack s;
	s = initStack(g->vextexNum);
	push(&s,start);
	is_visited[start] = true;
	while(!is_empty(&s))
	{
		int vertex;
		pop(&s,&vertex);
		int adjacentNumber;
		int* adjacentVertex = findAdjacentVertex(g,vertex,&adjacentNumber);
		for(int i = 0;i < adjacentNumber;++i)
		{
			if(false == is_visited[adjacentVertex[i]])
			{
				push(&s,adjacentVertex[i]);
				is_visited[adjacentVertex[i]] = true;
			}
		}
		printf("%c ",g->vextex[vertex]);
	}
	printf("\n");
	destroyStack(&s);
	free(is_visited);
}

//寻找与vex相邻的节点,通过n带出结点个数,返回这些节点组成的数组
int* findAdjacentVertex(Graph* g,int vex,int *n)
{
	//计数器
	int cnt = 0;
	int* adj = (int*)malloc(sizeof(int) * g->vextexNum);
	for(int i = 0;i < g->vextexNum;++i)
	{
		if(1 == g->matrix[vex][i])
			adj[cnt++] = i;
	}
	*n = cnt;
	return adj;
}

void BFS_byQueue(Graph *g,char Start)
{
	int start = Start-'A';
	printf("使用栈进行广度优先遍历:\n依次访问节点:");
	//建立数组来标记节点是否被访问过
	bool *is_visited = (bool*)malloc(sizeof(bool) * g->vextexNum);
	for(int i = 0;i < g->vextexNum;++i)
		is_visited[i] = false;
	Queue q;
	q = initQueue(g->vextexNum);
	enqueue(&q,&start);
	is_visited[start] = true;
	while(!is_empty(&q))
	{
		int vertex;
		dequeue(&q,&vertex);
		int adjacentNumber;
		int* adjacentVertex = findAdjacentVertex(g,vertex,&adjacentNumber);
		for(int i = 0; i < adjacentNumber;++i)
		{
			if(false == is_visited[adjacentVertex[i]] )
			{
				enqueue(&q,&adjacentVertex[i]);
				is_visited[adjacentVertex[i]] = true;
			}
		}
		printf("%c ",g->vextex[vertex]);
	}
	free(is_visited);
	destroyQueue(&q);
}


注意到,图的深度优先遍历既可以使用栈来实现,也可以使用对归来实现。这与我们之前的认识是相一致的。

程序中使用的栈和队列的代码就不往出贴了,之前的博客都介绍过。主要区别在于这次是通过函数的返回值来获得一个栈或者队列,以前是把指向它们的指针传到函数里。但是二者的思想是大同小异的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值