实现无向图的深度优先搜索与广度优先搜索算法(DFS与BFS)

()程序内容:

1、队列结构与其中几种基本用法

2、无向图的存储方式

3、无向图的深度优先搜索算法

4、无向图的广度优先搜索算法

代码:

duilie.h:

#include <stdio.h>
#include <stdlib.h>
//建立链表队列,输入 10 个数,依次入队,输出队头元素,再依次出队打印输出。
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
//链队列结构类型定义
typedef int QElemType;
typedef struct Qnode
{ //链队列结点的类型定义
 QElemType data; 
 struct Qnode *next;
}QNode,*QueuePtr;
typedef struct 
{ //链队列的表头结点的的类型定义
 QueuePtr front; //队头指针,指向链表的头结点
 QueuePtr rear; //队尾指针,指向队尾结点
}LinkQueue;
//链队列初始化函数定义
Status InitQueue(LinkQueue &Q)
{
	Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));			//建立头结点 
	if(!Q.front)	exit(OVERFLOW);
	Q.front->next = NULL;
	return OK;
}
//链队列销毁函数定义
Status DestroyQueue(LinkQueue &Q)
{
	while(Q.front){					//若头结点不为空则继续清除 
		Q.rear = Q.front->next;     //让队尾指向队头 下一项 
		free(Q.front);				//释放队头指向 
		Q.front = Q.rear;			//让队头指向队尾指向 
	}
	return OK; 
}
//链队列插入队尾元素函数定义
Status EnQueue(LinkQueue &Q,QElemType e)
{
	QueuePtr p = (QueuePtr)malloc(sizeof(QNode));		//临时变量存放 
	if(!p)	
	{
		printf("分配队列元素空间失败");
		exit(ERROR);
	}
	p->data = e;
	p->next = NULL;
	Q.rear->next = p;
	Q.rear = p;
	return OK;
}
//取链队列队头元素函数定义
Status GetHead(LinkQueue Q,QElemType &e)
{
	if(!Q.front->next) exit(ERROR);			//若Q.front下一项不为空则输出数据 
	e = Q.front->next->data; 
}
//链队列删除队头元素函数定义
Status DeQueue(LinkQueue &Q,QElemType &e)
{
	QueuePtr p;
	if(!Q.front->next) 
		return(ERROR);			//若Q.front下一项不为空则进行 
	p = Q.front->next;
	e = p->data;
	Q.front->next = p->next;
	free(p);
	if(!Q.front->next)
		Q.rear = Q.front;
}

graph.cpp:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "duilie.h"
//typedef VertexType int;
typedef enum {DG,DN,AG,AN} GraphKind;
typedef struct ArcNode
{ //边(弧)结点的类型定义
 int adjvex; //边(弧)的另一顶点的在数组中的位置
 struct ArcNode *nextarc; //指向下一条边(弧)结点的指针
}ArcNode,*ANList;
typedef struct Vnode 
{ //顶点结点和数组的类型定义
char *data; //顶点信息
ANList finrstarc; //指向关联该顶点的边(弧)链表
}Vnode, *AjList;
typedef struct 
{
AjList vertices;//AjList vertices
int vexnum, arcnum; //图的当前顶点数和弧数
GraphKind kind; //图的种类标志
}ALGraph;

//字符串动态输入
char *strInput(){
	int n=1;
	char *array;
	char c;
	array = (char *)malloc(sizeof(char));
	if(!array) 
	{
		printf("字符串输入分配空间失败");
		exit(0);
	}
	fflush(stdin);//清除缓冲区 
 	while((c = getchar()) != '\n')
	{
		array = (char *)realloc(array,sizeof(char)*++n);
		if(!array) {
			printf("字符串重新分配空间失败");
			exit(0);	
		}
		array[n-2] = c;
	}
	array[n-1] = '\0';
	return array;
} 

//寻找编号对应下标
int seekSub(ALGraph &graph,char *n)
{
	for(int i=0;i<graph.vexnum;i++)
	{
		if(!strcmp(graph.vertices[i].data,n))
		{
			return i;
		}
	}
	return 0;
} 

//创建无向图 
void createDG(ALGraph &graph)
{
	int num;
	graph.kind = DG;
	graph.arcnum = 0; 
	printf("请输入顶点个数:");
	fflush(stdin);//清除缓冲区 
	scanf("%d",&num);
	graph.vexnum = num;
	graph.vertices = (AjList)malloc(sizeof(Vnode)*num);
	if(!graph.vertices)
	{
		printf("图顶点分配失败");
		exit(0);
	}
	for(int i=0;i<num;i++)
	{
		printf("请输入第%d个顶点名字",i+1);
		graph.vertices[i].data = strInput();
		graph.vertices[i].finrstarc = NULL;
	}
	for(int i=0;i<num;i++)
	{
		int n;
		int again=0;
		printf("请输入与顶点%s相连的边数量:",graph.vertices[i].data);
		scanf("%d",&n);
		graph.arcnum += n;
		if(n != 0)
		{
			for(int j=0;j<n;j++)
			{
				char *temp;
				ANList p;
				if(again==0)
				printf("请输入第%d个与顶点%s有关的点名字:",j+1,graph.vertices[i].data);
				else{
					printf("请重新输入第%d个与顶点%s有关的点名字:",j+1,graph.vertices[i].data);
					again = 0;
				}
				temp = strInput();
				for(int f=0;f<num;f++)
				{
					if(!strcmp(graph.vertices[f].data,temp))
					{
						if(j==0)
						{
							p = graph.vertices[i].finrstarc = (ANList)malloc(sizeof(ArcNode));
							if(!p)
							{
								printf("分配边空间失败");
								exit(0);
							}
							p->adjvex = f;
							p->nextarc = NULL;
							break;
						}
						else
						{
							p->nextarc = (ANList)malloc(sizeof(ArcNode));
							if(!p->nextarc)
							{
								printf("分配边空间失败");
								exit(0);
							}
							p = p->nextarc ;
							p->adjvex = f;
							p->nextarc = NULL;
							break;
						}
					}		
					if(f == num-1)
					{
						printf("未找到编号%s顶点\n",temp);
						again = 1;
						j--;
					}
				}
			}
		}
	}
	graph.arcnum /= 2;
}

//展示无向图结构 
void showDG(ALGraph &graph)
{
	ANList p = NULL;
	for(int i=0;i<graph.vexnum;i++)
	{
		p = graph.vertices[i].finrstarc;
		printf("%s-->",graph.vertices[i].data);
		if(!p)
		printf("NULL");
		while(p)
		{
			printf("%s ",graph.vertices[p->adjvex].data);
			p = p->nextarc;
		}
		printf("\n");
	}
}

//深度优先遍历 
void DepthFirstSearch(ALGraph g,int i,int *visit,int *searchNum)
{
	ANList p;
	p = g.vertices[i].finrstarc;
	searchNum[searchNum[0]] = i;
	searchNum[0]++;
	visit[i] = 1;
	while(p)
	{
		if(visit[p->adjvex]==0)
		{
			DepthFirstSearch(g,p->adjvex,visit,searchNum);	
		}
		p = p->nextarc;
	}
}

//广度优先遍历 
void BreadthFirstSearch(ALGraph g,int i,int *visit,int *searchNum)
{
	int p = i;
	ANList pt;
	LinkQueue link;
	InitQueue(link);
	EnQueue(link,p);
	searchNum[searchNum[0]]=p;
	searchNum[0]++;
	visit[p] = 1;
	while(link.front->next!=NULL)
	{
		DeQueue(link,p);
		pt = g.vertices[p].finrstarc;
		if(pt!=NULL)
		p = pt->adjvex;
		while(pt != NULL)
		{
			if(!visit[p])
			{
				EnQueue(link,p);
				searchNum[searchNum[0]] = p;
				searchNum[0]++;
				visit[p] = 1;
			}
			pt = pt->nextarc;
			if(pt!=NULL)
			p = pt->adjvex;
		}
	}
}

//图遍历
int *GraphSearch(ALGraph g,char *i,void (*fun)(ALGraph,int,int *,int *))//参数 i 表示顶点名字 
{	
	int *searchNum;
	int sub;
	int state = 1;
	int num = g.vexnum;
	int visit[num]={0};
	searchNum = (int*)malloc(sizeof(int)*(g.vexnum+1));
	searchNum[0] = 1;
	sub = seekSub(g,i);
	while(state==1)
	{
		(*fun)(g,sub,visit,searchNum);
		for(int i=0;i<num;i++)
		{
			if(visit[i]==0)
			{
				sub = i;
				break;
			}
			if(i==num-1)
			{
				state = 0;
			}
		}
	}
	return searchNum;
}


int main()
{
	ALGraph graph;
	char *n;
	int *searchNum;
	
	//建立无向图 
	createDG(graph);
	
	//展示无向图 
	showDG(graph);
	
	//深度优先遍历图 
	printf("深度优先遍历\n输入从起始点名字:");
	n = strInput();
	searchNum = GraphSearch(graph,n,DepthFirstSearch);
	for(int i=1;i<=graph.vexnum;i++)
	{
		printf("%s",graph.vertices[searchNum[i]].data);
		if(i != graph.vexnum)
		printf("-->");
		else
		printf("\n");
	}
	free(n);
	free(searchNum);
	
	
	//广度优先遍历图 
	printf("广度优先遍历\n输入从起始点名字:");
	n = strInput();
	searchNum = GraphSearch(graph,n,BreadthFirstSearch);
	for(int i=1;i<=graph.vexnum;i++)
	{
		printf("%s",graph.vertices[searchNum[i]].data);
		if(i != graph.vexnum)
		printf("-->");
	}
	free(n);
	free(searchNum);
	
	
	return 0;
}

按照该图输入图

输入结果:

 

因广度优先遍历需用到队列,故在graph.cpp中引用了队列头文件duilie.h,对应代码在上有显示。

依旧对程序功能进行了一定程度的封装,将功能放进函数中,提高了代码复用性。

根据深度优先遍历与广度优先遍历的代码,可知两个的外壳是一致的,所以对这层代码进行了单独封装成了函数(即GraphSearch函数),该函数需要传入函数指针,该函数是进行广度优先还是深度优先遍历取决于传入的参数是DepthFirstSearch(即深度优先函数)还是BreadthFirstSearch(即广度优先函数),该程序只展示了无向图,该层外壳还可推广运用在无向网、有向图、有向网中的深度与广度优先遍历,进一步提高了代码的复用性。

关于visit[]数组(用于记录点是否已被读取)使用全局变量还是作为参数传入函数两种方法的讨论:

于个人理解,该图的建立与图的遍历等都封装为函数,最终目的为了将其放入头文件中,需要用该方法时预处理进行引用即可,所以若作为函数中调用其他分节函数时用到的参数,可直接使用函数,无其他顾虑。若是作为全局变量写在头文件中,当引用头文件时需要考虑该变量是否没有被重复定义,头文件中并不推荐使用全局变量。总结,个人更加推荐作为参数传入函数的方法。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值