数据结构(二)——栈和队列之DFS和BFS遍历迷宫

代码目的:通过栈和队列分别实现迷宫的DFS遍历并输出可到达的全部点和BFS遍历输出走出迷宫的最短路径

在迷宫输入到电脑中,以?表示起点,以0表示可走路径,以#表示墙,既不可以走的路径,以*表示可以到达的路径。
一、利用栈实现迷宫的深度遍历并打印路径
1.首先运用一个for循环找到迷宫的起点,将起点存入栈中。
2.再运用一个while循环,依次寻找栈顶元素的上,右,下,左,是否为可到达路径并且未走过,若满足其中一个方向为可到达路径并且未走过时,就直接将其入栈,进行下一次寻找;若四个方向均不满足以上条件,则将栈顶元素出栈,进行下一次查找。
3.直到栈为空时,将遍历过的地方打印出来。
代码中dir[][]数组表示方向,vis[][]数组用于标记走过的点,maze[][]数组用于存储迷宫。

void DFS(char maze[maxn][maxn], int n, int m, char vis[maxn][maxn]){
/*
  深度优先遍历的过程
  请在本过程中求出从"?"能到达的点数
  并将所有能到达的点用"$"覆盖
*/
    int i,j,f=0,i1,j1;
	stack *st = InitStack();
    for(i=0;i<n;i++)
    {
        for(j=0;j<m;j++)
        {
            vis[i][j]=maze[i][j];//复制迷宫
        }
    }
	for(i=0;i<n;i++)
    {
        for(j=0;j<m;j++)
        {
            if(vis[i][j]=='?')//寻找起点
            {
                Push(st,i,j,f);//存储起点的位置
                st->size--;//不计入节点的步数
            }
        }
    }
    while(EmptyStack(st))//当栈为空时结束
    {
        if(LegalPosition(vis,st->top->x+dir[0][0],st->top->y+dir[0][1],n,m))//判断该点的右边是否能走
       {
           st->top=Push(st,st->top->x+dir[0][0],st->top->y+dir[0][1],1);
           vis[st->top->x][st->top->y]='$';
       }
            else if(LegalPosition(vis,st->top->x+dir[1][0],st->top->y+dir[1][1],n,m))//判断该点的下边是否能走
                    {
                        st->top=Push(st,st->top->x+dir[1][0],st->top->y+dir[1][1],2);
                        vis[st->top->x][st->top->y]='$';
                    }
                    else if(LegalPosition(vis,st->top->x+dir[2][0],st->top->y+dir[2][1],n,m))//判断该点的左边是否能走
                            {
                                st->top=Push(st,st->top->x+dir[2][0],st->top->y+dir[2][1],3);
                                vis[st->top->x][st->top->y]='$';
                            }
                            else if(LegalPosition(vis,st->top->x+dir[3][0],st->top->y+dir[3][1],n,m))//判断该点的上边是否能走
                                    {
                                        st->top=Push(st,st->top->x+dir[3][0],st->top->y+dir[3][1],4);
                                        vis[st->top->x][st->top->y]='$';
                                    }
                                    else
                                        {
                                            st->top=Pop(st);//若都不行,则出栈,并退一步
                                        }
    }
    for(i1=0;i1<n;i1++)
    {
			for(j1=0;j1<m;j1++)
            {
				printf("%c", vis[i1][j1]);//打印走完后的迷宫
            }
				printf("\n");
    }
    printf("能走到的点的个数为: %d\n \n",st->size);
	memset(vis, 0, sizeof(vis));//清除数组
}

二、利用队列实现迷宫的广度遍历并打印最短路径
1.首先运用一个for循环找到迷宫的起点,并将其存入队列。
2.将队列中的队头元素出队,检测其上、右、下、左是否为可到达路径且未走过。若是,则入队,继续检测;若不是,则不入队,继续检测。直到四个方向均扫描过后,对下一个队头元素进行扫描。直到扫描到了出口或者是队列为空。则打印相应的结果。
3.在打印结果的时候,我运用了类似于树的数据结构,对最后的打印结果进行了回溯。在代码中有体现。

void BFS(char maze[maxn][maxn], int n, int m,char vis[maxn][maxn]){
/*
  广度优先遍历的过程
  请在本过程中求出从"?"出发走出迷宫的最短路长度
  并将最短路径用"*"覆盖
*/
    int i,j,ret,i1,j1;
	Queue *que = InitQueue();//初始化队列
	for(i=0;i<n;i++)
    {
        for(j=0;j<m;j++)
        {
            vis[i][j]=maze[i][j];//复制迷宫
        }
    }
    for(i=0;i<n;i++)
    {
        for(j=0;j<m;j++)
        {
            if(vis[i][j]=='?')//寻找起点
            {
                que->back=EnQueue(que,i,j);//起点入队
                que->front=que->back;
            }
        }
    }
    while(isEmpty(que)&&ret!=2)
    {
        for(i1=0;i1<4;i1++)
        {
            ret=LegalPosition1(vis,que->front->x+dir[i1][0],que->front->y+dir[i1][1],n,m);//判断是否为合法得点点
            if(ret==2)//找到出口
            {
                printf("Success!\n");
                break;
            }
            if(ret==1)//点合法
            {
                que->back=EnQueue(que,que->front->x+dir[i1][0],que->front->y+dir[i1][1]);//入队
                vis[que->back->x][que->back->y]='*';//标记走过的点
            }
        }
        if(ret!=2)//还没找到出口
        {
            que->front=DeQueue(que);
        }
    }
    if(ret!=2)//没找到出口
    {
        printf("Impossible!!!\n");
    }
    else//找到了出口
    {
        while(maze[que->front->x][que->front->y]!='?')//进行回溯寻找路径
        {
            maze[que->front->x][que->front->y]='*';
            que->size++;
            que->front=que->front->prev;//回溯到上一个结点的
        }
        for(i1=0;i1<n;i1++)
        {
                for(j1=0;j1<m;j1++)
                {
                    printf("%c", maze[i1][j1]);//打印最短路径
                }
                    printf("\n");
        }
        printf("the shortest load is %d\n",que->size);
    }
	memset(vis, 0, sizeof(vis));
	memset(maze,0,sizeof(maze));
}

三、附上总代码
由于博主的迷宫存在一个文件夹里,故主函数里用了文件流。使用代码的时候需要在主函数里有些修改。

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include<assert.h>
#define maxn 1002
/*
maze数组用于存储迷宫
vis数组用于在遍历的时候标记已经访问过的点
dir数组用于表示上下左右方向
n和m分别表示迷宫的行数和列数
*/
char maze[maxn][maxn];
int vis[maxn][maxn];
int dir[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};
int n,m;

int LegalPosition(char maze[maxn][maxn],int x, int y,int n,int m){
/* 判断(x,y)位置是否合法*/
    if(x<0||x>n-1||y<0||y>m-1)//是否越界
    {
        return 0;//位置合法
    }
    if(maze[x][y]=='#'||maze[x][y]=='$'||maze[x][y]=='?')//是否为墙或覆盖过的点
    {
        return 0;//位置不合法
    }
    else
    {
        return 1;//位置合法
    }
}

int LegalPosition1(char maze[maxn][maxn],int x, int y,int n,int m){
/* 判断(x,y)位置是否合法*/
    if(x<0||x>n-1||y<0||y>m-1)
    {
        return 2;//找到出口
    }
    if(maze[x][y]=='#'||maze[x][y]=='*'||maze[x][y]=='?')//是否为墙或覆盖过的点
    {
        return 0;//位置不合法
    }
    else
    {
        return 1;//位置合法
    }
}
typedef struct node{
/* 栈中存储的节点 */
	int x; // 行坐标
	int y; // 列坐标
	int flag; // 当前方向
	struct node *next; // 栈中上一个节点的指针
/* 可自由添加需要用的变量 */

}node,*pnode;

typedef struct stack{
/* 栈 */
	node *top; // 栈顶指针
	int size; // 栈中节点个数
	node *buttom;//栈底指针
/* 可自由添加需要用的变量 */

}stack,*pstack;

pstack InitStack(){
/* 初始化栈 */
	pstack st;
    st=(pstack)malloc(sizeof(stack));
    assert(st);//判断申请是否成功
    st->top=st->buttom;
    st->size=0;
    return st;
}
int EmptyStack(pstack st)//判栈空
{
    if(st->top==st->buttom||st==NULL)//判断是否为空
    {
        return 0;
    }
    else
    {
        return 1;//不为空,返回真
    }
}
pnode Push(pstack st,int x1,int y1,int flag1)//入栈
{
    pnode n=(pnode)malloc(sizeof(node));//申请新结点
    n->x=x1;
    n->y=y1;
    n->flag=flag1;//flag 1.2.3.4 分别代表上下左右
    n->next=st->top;
    st->top=n;
    st->size++;
    return st->top;//返回入栈后的头结点
}
pnode Pop(pstack st)//出栈
{
    pnode n;
    if(st->top==st->buttom)//判断是否为空
    {
        printf("The stack is NULL\n");
        return;
    }
    else
    {
        n=st->top;
        st->top=st->top->next;
        free(n);
        n=NULL;
        return st->top;
    }
}

void DFS(char maze[maxn][maxn], int n, int m, char vis[maxn][maxn]){
/*
  深度优先遍历的过程
  请在本过程中求出从"?"能到达的点数
  并将所有能到达的点用"$"覆盖
*/
    int i,j,f=0,i1,j1;
	stack *st = InitStack();
    for(i=0;i<n;i++)
    {
        for(j=0;j<m;j++)
        {
            vis[i][j]=maze[i][j];//复制迷宫
        }
    }
	for(i=0;i<n;i++)
    {
        for(j=0;j<m;j++)
        {
            if(vis[i][j]=='?')//寻找起点
            {
                Push(st,i,j,f);//存储起点的位置
                st->size--;//不计入节点的步数
            }
        }
    }
    while(EmptyStack(st))//当栈为空时结束
    {
        if(LegalPosition(vis,st->top->x+dir[0][0],st->top->y+dir[0][1],n,m))//判断该点的右边是否能走
       {
           st->top=Push(st,st->top->x+dir[0][0],st->top->y+dir[0][1],1);
           vis[st->top->x][st->top->y]='$';
       }
            else if(LegalPosition(vis,st->top->x+dir[1][0],st->top->y+dir[1][1],n,m))//判断该点的下边是否能走
                    {
                        st->top=Push(st,st->top->x+dir[1][0],st->top->y+dir[1][1],2);
                        vis[st->top->x][st->top->y]='$';
                    }
                    else if(LegalPosition(vis,st->top->x+dir[2][0],st->top->y+dir[2][1],n,m))//判断该点的左边是否能走
                            {
                                st->top=Push(st,st->top->x+dir[2][0],st->top->y+dir[2][1],3);
                                vis[st->top->x][st->top->y]='$';
                            }
                            else if(LegalPosition(vis,st->top->x+dir[3][0],st->top->y+dir[3][1],n,m))//判断该点的上边是否能走
                                    {
                                        st->top=Push(st,st->top->x+dir[3][0],st->top->y+dir[3][1],4);
                                        vis[st->top->x][st->top->y]='$';
                                    }
                                    else
                                        {
                                            st->top=Pop(st);//若都不行,则出栈,并退一步
                                        }
    }
    for(i1=0;i1<n;i1++)
    {
			for(j1=0;j1<m;j1++)
            {
				printf("%c", vis[i1][j1]);//打印走完后的迷宫
            }
				printf("\n");
    }
    printf("能走到的点的个数为: %d\n \n",st->size);
	memset(vis, 0, sizeof(vis));//清除数组
}


typedef struct qnode{
/* 队列中存储的节点 */
	int x; // 行坐标
	int y; // 列坐标
	struct qnode *next; //队列中下一个节点的指针
	struct qnode *prev; //先驱结点
/* 可自由添加需要用的变量 */

}QueueNode,*pQueueNode;

typedef struct queue{
/* 队列 */
	QueueNode *front; //队首指针
	QueueNode *back; //队尾指针
	int size; // 队列中节点个数
/* 可自由添加需要用的变量 */

}Queue,*pQueue;

Queue *InitQueue(){
/* 初始化队列 */
	Queue* que = (Queue *)malloc(sizeof(Queue));//申请空间
	assert(que);//判断是否申请成功
	que->back=NULL;
	que->front=que->back;
	que->size = 0;
	return que;
}
int isEmpty(Queue *que){
/* 判断队列是否为空 */
    if(que->back==NULL)
    {
        return 0;//为空
    }
    else
    {
        return 1;//不为空
    }
}
pQueueNode EnQueue(pQueue que,int x1,int y1)
{
/* 入队 */
    pQueueNode n=(pQueueNode)malloc(sizeof(QueueNode));//申请空间
    n->x=x1;
    n->y=y1;
    if(que->back==NULL)//队列为空的情况下进行入队
    {
        que->back=n;
        que->front=n;
        n->prev=NULL;
        return que->back;
    }
    else//队列不为空的情况下进行入队
    {
        que->back->next=n;
        n->prev=que->front;
        que->back=n;
        return que->back;
    }

}
pQueueNode DeQueue(Queue *que){
/* 出队 */
    if(que->back==que->front)//队列只剩一个结点的情况下
    {
        que->back=NULL;
        que->front=que->back;
        return que->front;
    }
    else//队列还有大于等于二个的结点
    {
        que->front=que->front->next;
        return que->front;
    }
}


void BFS(char maze[maxn][maxn], int n, int m,char vis[maxn][maxn]){
/*
  广度优先遍历的过程
  请在本过程中求出从"?"出发走出迷宫的最短路长度
  并将最短路径用"*"覆盖
*/
    int i,j,ret,i1,j1;
	Queue *que = InitQueue();//初始化队列
	for(i=0;i<n;i++)
    {
        for(j=0;j<m;j++)
        {
            vis[i][j]=maze[i][j];//复制迷宫
        }
    }
    for(i=0;i<n;i++)
    {
        for(j=0;j<m;j++)
        {
            if(vis[i][j]=='?')//寻找起点
            {
                que->back=EnQueue(que,i,j);//起点入队
                que->front=que->back;
            }
        }
    }
    while(isEmpty(que)&&ret!=2)
    {
        for(i1=0;i1<4;i1++)
        {
            ret=LegalPosition1(vis,que->front->x+dir[i1][0],que->front->y+dir[i1][1],n,m);//判断是否为合法得点点
            if(ret==2)//找到出口
            {
                printf("Success!\n");
                break;
            }
            if(ret==1)//点合法
            {
                que->back=EnQueue(que,que->front->x+dir[i1][0],que->front->y+dir[i1][1]);//入队
                vis[que->back->x][que->back->y]='*';//标记走过的点
            }
        }
        if(ret!=2)//还没找到出口
        {
            que->front=DeQueue(que);
        }
    }
    if(ret!=2)//没找到出口
    {
        printf("Impossible!!!\n");
    }
    else//找到了出口
    {
        while(maze[que->front->x][que->front->y]!='?')//进行回溯寻找路径
        {
            maze[que->front->x][que->front->y]='*';
            que->size++;
            que->front=que->front->prev;//回溯到上一个结点的
        }
        for(i1=0;i1<n;i1++)
        {
                for(j1=0;j1<m;j1++)
                {
                    printf("%c", maze[i1][j1]);//打印最短路径
                }
                    printf("\n");
        }
        printf("the shortest load is %d\n",que->size);
    }
	memset(vis, 0, sizeof(vis));
	memset(maze,0,sizeof(maze));
}


int main()
{
/* 从文件maze.in中读入 */
    freopen("maze.in","r",stdin);
	int o=0,i,j;
	printf("Please input data:\n");
	while (scanf("%d %d",&n,&m)!=EOF){
	// 可能有多组输入数据
		o += 1;
		for (i=0;i<n;i++)
			for (j=0;j<m;j++)
				scanf(" %c", &maze[i][j]);
		printf("--------Case #%d--------\n", o);
		DFS(maze, n, m,vis);
		BFS(maze, n, m,vis);
	}
	 fclose(stdin);
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值