栈的应用--迷宫问题

问题描述:给定一个迷宫,给定入口和出口,找到从入口到出口的一条路径(任何一条路径都可以),迷宫为0表示可走,为1表示墙。用1将迷宫围起来避免边界问题。

实现思路:1.DFS搜索(递归)

2.采用的数据结构

下面分别用这两种方法来解决这个问题。

DFS搜索(即递归+回溯)

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <unistd.h>
#define ROW 9
#define COL 9int integer[ROW][COL]={     //表示迷宫{1,0,1,1,1,1,1,1,1},{1,0,1,1,1,0,0,0,1},{1,0,0,0,0,0,1,0,1},{1,0,1,1,1,0,1,0,1},{1,0,1,0,0,0,1,0,1},{1,0,1,1,1,0,1,0,1},{1,0,0,0,0,1,1,0,1},{1,0,1,1,1,1,1,0,0},{1,1,1,1,1,1,1,1,1}
};
/*int integer[ROW][COL]={{2,0,2,2,2,2,2,2,2,2},//1{2,0,0,2,0,0,0,2,0,2},//2{2,0,0,2,0,0,0,2,0,2},//3{2,0,0,0,0,2,2,0,0,2},//4{2,0,2,2,2,0,0,0,0,2},//5{2,0,0,0,2,0,0,0,0,2},//6{2,0,2,0,0,0,2,0,0,2},//7{2,0,2,2,2,0,2,2,0,2},//8{2,2,0,0,0,0,0,0,0,0},//9{2,2,2,2,2,2,2,2,2,2} //10
}; 大家可以用这个迷宫进行再次观察*/int  print(int integer[ROW][COL],int x,int y);//打印该迷宫int dir[4][2]={
{1,0},{-1,0},
{0,1},{0,-1},
}  ;     //方向数组,代表 4 个方向int visted[120][120] ;    //  1  代表访问过  0 代表没有访问过int check(int x,int y) //检查下一步是否越界和是否已经走过以及是否是墙
{if(x< 0 || y<0 || x>= ROW || y>= COL)  return 0;if(visted[x][y])return 0;if(integer[x][y] !=  0 )return 0;return 1;
}int dfs(int x,int y)    //已经踏到了  x , y ,即x,y  可踏
{int xx,yy ,i ;usleep(100000);printf("\033c");print(integer,x,y);if(x ==  7 && y == 8 )exit(0);for(i= 0;i< 4 ;i++)   // 4 个方向 {xx =  x + dir[i][0];yy =  y + dir[i][1];if(check(xx,yy))    //xx ,yy 可踏上去{visted[xx][yy]= 1;dfs(xx,yy) ;visted[xx][yy] = 0 ;  //回溯}}usleep(100000); //再次打印,显示回溯的效果printf("\033c");print(integer,x,y);return 0;
}int print(int integer[ROW][COL],int x,int y)
{int i,j;for(i=0;i<ROW ;i++){for(j=0 ;j<COL ;j++){if(visted[i][j])printf("\033[41;32m  *  \033[0m") ;else printf("  %d  ",integer[i][j]);}printf("\n\n");}
}
int main(void)
{int i,j ;memset(visted,0,sizeof(visted));visted[0][1]=1;      //从入口出发dfs(0,1) ; 
}

运行截图:

这里写图片描述

PS 1.这是一个动态演示的程序,可以清晰的看到移动的动作,所以运行有奇效

2. 回溯之后要再打印一次,才能有回溯的效果,并且必须有sleep 函数,否则会因为程序运行太快而导致看不到回溯的效果。

3. 如果对于DFS搜索还不太懂的–>点这里,文中提到的马踏棋盘我会在下一篇博客中提到。

采用的数据结构

先来提出几个问题

1.为什么要用栈来实现?有什么好的地方?
2.di 有什么作用?为什么要它?
3.栈空与栈不空,有什么用?
4.大体思路是什么?
#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>
#define  MAXSTACKSIZE  100  //栈的大小
#define      N          10     //二维迷宫大小
#define  Entrance_row   0  //入口
#define  Entrance_col   1 
#define  Exit_row   8   //出口
#define  Exit_col   9
typedef struct position{   //坐标   int x;int y;
}position ;
typedef struct SElement {position p;   int di;     //记录已经走了多少个方向
}SElement ; 
typedef struct Mystack{SElement  *top;SElement  *base;int stackSize ;
}Mystack ;int Maze[N][N]={{2,0,2,2,2,2,2,2,2,2},//1{2,0,0,2,0,0,0,2,0,2},//2{2,0,0,2,0,0,0,2,2,2},//3{2,0,0,0,0,2,2,0,0,2},//4{2,0,2,2,2,0,2,0,2,2},//5{2,0,0,0,2,0,0,0,0,2},//6{2,0,2,0,0,0,2,0,0,2},//7{2,0,2,2,2,0,2,2,0,2},//8{2,2,0,0,0,0,0,0,0,0},//9{2,2,2,2,2,2,2,2,2,2} //10
};int IsEmptyStack(Mystack *path);int InitStack(Mystack *path)   // top ,base  ,size 
{path->top = path->base =(SElement *)malloc(sizeof(SElement)*MAXSTACKSIZE);if(path->top == NULL ){printf("Init  stack is failed !!! \n");return -1;}path->stackSize = MAXSTACKSIZE;return 0;
}int pop(Mystack *path ,SElement *t)  //从path 中出一个元素给t 
{if(IsEmptyStack(path) == 1)return 0;*t = *(path->top-1);path->top-- ;return 1;
}int push(Mystack *path ,SElement p) //入栈
{*(path->top) = p ;path->top++;
}int IsEmptyStack(Mystack *path)
{if(path->top == path->base )   return 1;  //空栈返回 1  else return 0 ;
}
int print_MAZE(int Maze[N][N])  //打印迷宫
{int i,j;for(i= 0 ;i< N;i++){for(j= 0 ;j< N ;j++){if(Maze[i][j] == 10)    printf("\033[31m  *  \033[0m") ;else  printf("  %d  ",Maze[i][j]);}printf("\n\n");}
}
int check(position now_try) //检查下一步是否越界和是否是墙 
{if(Maze[now_try.x][now_try.y]  !=  0)  //0  代表走的通return 0;if(now_try.x <  0 && now_try.x >=  N  )return 0;if(now_try.y <  0 && now_try.y >=  N  )return 0;return 1;
}position   NextPosition(position  now_try ,int direction)  //获得下一个位置的坐标 x,y
{position next ;next.x= now_try.x;next.y  = now_try.y;switch(direction){case 4:next.y+=1;break; //东case 3:next.x+=1;break; //南case 1:next.x-=1;break;//西case 2:next.y-=1;break;//北}return next ;
}
int main(void)
{print_MAZE(Maze) ;Mystack  path ;InitStack(&path);position  now_try ; //所尝试的位置now_try.x= Entrance_row;now_try.y= Entrance_col;do{if(check(now_try)) //进入if 语句就说明这个点能走,就把他赋值为10 ,入栈,找下一步,继续{Maze[now_try.x][now_try.y]  =10 ;SElement temp ;temp.p.x= now_try.x;temp.p.y= now_try.y;push(&path,temp);if(now_try.x == Exit_row && now_try.y == Exit_col )break;now_try  = NextPosition(now_try,1);  //先向一个方向进行探索printf("\033c"); // 动态演示所走的路的语句print_MAZE(Maze);usleep(800000);}else     //这个点为 2 ,不能走,那么就取出它的上一个(即栈顶元素),寻找其他方向{if(IsEmptyStack(&path) !=  1)  //栈不空{SElement t ;pop(&path,&t);    //要在被调函数中改变twhile(t.di == 4 && IsEmptyStack(&path) !=  1){   //检查是否四个方向都已经被走过Maze[t.p.x][t.p.y] = 9 ;   //9 代表已经被探索过的路pop(&path,&t);}if(t.di < 4) //如果四个方向没有走够,就换一个方向走{now_try = NextPosition(t.p,t.di+1);t.di++;push(&path,t);}}}}while( IsEmptyStack(&path) ==  0  );  //0 就是有元素printf("\033c");print_MAZE(Maze);return 0;
}

运行截图:

这里写图片描述

问题解答:

1.首先我们都知道栈有先进后出的特点,那么我们的迷宫是否需要这种特点呐。如果走的通,那么就走,如果走不通,那是不是要回到前一步,找另外一个方向走。那么前一步怎么存储?是不是符合一个先存后取的顺序!OK !这不正好与我们的栈的特点重合吗。

2.di 的作用有两个。一是表示方向,二是表示走了几个方向了。是不是感觉很拗口。那么我来简单解释一下。用1,2,3,4来表示东南西北,如果di==3,那么就说明北面还没有走,如果di == 4,那么就说明四个方向都已经走过了。

3.栈空与栈不空,有什么用?假如我们将迷宫改成了这样,那么会发生什么?

int Maze[N][N]={{2,0,2,2,2,2,2,2,2,2},{2,0,2,2,2,2,2,2,2,2},{2,0,2,2,2,2,2,2,2,2},{2,0,2,2,2,2,2,2,2,2},{2,0,2,2,2,2,2,2,2,2},{2,0,2,2,2,,2,2,2,2},{2,0,2,2,2,2,2,2,2,2},{2,0,2,2,2,2,2,2,2,2},{2,2,2,2,2,2,2,2,2,2},{2,2,2,2,2,2,2,2,2,2} 
};

是不是会依次入栈,然后依次出栈,出栈之后是不是会栈空,如果不判断栈空的话是不是会陷入一种死循环的状态呐。

4.核心代码:

    now_try.x= Entrance_row;now_try.y= Entrance_col;do{if(check(now_try)) //进入if 语句就说明这个点能走,就把他赋值为10 ,入栈,找下一步,继续{Maze[now_try.x][now_try.y]  =10 ;SElement temp ;temp.p.x= now_try.x;temp.p.y= now_try.y;push(&path,temp);if(now_try.x == Exit_row && now_try.y == Exit_col )break;now_try  = NextPosition(now_try,1);  //先向一个方向进行探索printf("\033c"); // 动态演示所走的路的语句print_MAZE(Maze);usleep(800000);}else     //这个点为 2 ,不能走,那么就取出它的上一个(即栈顶元素),寻找其他方向{if(IsEmptyStack(&path) !=  1)  //栈不空{SElement t ;pop(&path,&t);    //要在被调函数中改变twhile(t.di == 4 && IsEmptyStack(&path) !=  1){   //检查是否四个方向都已经被走过Maze[t.p.x][t.p.y] = 9 ;   //9 代表已经被探索过的路pop(&path,&t);}if(t.di < 4) //如果四个方向没有走够,就换一个方向走{now_try = NextPosition(t.p,t.di+1);t.di++;push(&path,t);}}}}while( IsEmptyStack(&path) ==  0  );  //0 就是有元素

大体思路:

这里写图片描述

参考资料:参考资料

转载于:https://www.cnblogs.com/Tattoo-Welkin/p/10335326.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/415551.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

常用的方法论-PARR

转载于:https://www.cnblogs.com/qjm201000/p/7687470.html

修改wireshark协议解析规则

不同的协议有不同的解码器,wireshark尝试为每个包尝试找到正确的解码器,特定的情况有可能会选择错误的解码器。 1.使用了其它协议的标准端口&#xff0c;被错误解码&#xff0c;使用udp的80端口发送数据被当作QUIC协议解析。 wireshark菜单“Analyze–>Enabled Protocols…”…

Fiddler过滤指定域名

Fiddler过滤指定域名的方法一 切换到fiddler右侧窗口的Filters选项卡&#xff0c;勾选顶部的“Use Filters”&#xff0c;找到Hosts区域&#xff0c;设置以下三个选项&#xff1a; 1.第一项有三个选项&#xff0c;不做更改&#xff1a; “No zone filter” “Show Only Intrane…

JMeter插件模拟发送UDP请求:UDP sampler

JMeter安装UDP插件后支持发送UDP协议的请求包&#xff0c;官方介绍安装插件后可以用来测试DNS, NTP, TFTP, Boot servers and many-many other systems。 JMeter UDP插件下载地址&#xff1a;http://jmeter-plugins.org/downloads/all/&#xff0c;下载JMeterPlugins-Extras-X.…

前端学习(2625):vs安装

1、官网https://code.visualstudio.com/Download下载最新版 最好下载zip格式&#xff0c;如下图所示&#xff1a; 解压到非系统盘&#xff0c;文件夹最好不要出现中文和空格&#xff0c;我解压到D:\VSCode-win32-x64-1.31.1&#xff0c;直接运行code.exe即可 2、将vscode修改…

前端学习(2626):取消360为首页

浏览器右上角菜单栏中 工具--选项--基本设置--启动时打开--主页--输入您想设置的网址 即可

检查调试句柄泄漏

Windows任务管理查看句柄数 任务管理–>查看–>选择列–>勾选“句柄数” process explorer查看句柄详细信息 process explorer 中双击指定进程打开进程的properties窗口&#xff0c;切换到performance标签页&#xff0c;可以看到比任务管理器更详细的统计数据。 包括H…

小程序 获取 用户 unionid

首先明确&#xff0c;获取小程序的unionid需要在后台实现。下面开始详细介绍步骤。 1、首先要获得encryptedData&#xff0c;一种是通过授权函数&#xff0c;一种通过授权按钮 官方文档 https://developers.weixin.qq.com/miniprogram/dev/api/wx.getUserInfo.html?search-k…

前端学习(2627):node安装

1、在使用之前&#xff0c;先类掌握3个东西&#xff0c;明白它们是用来干什么的&#xff1a; npm: nodejs 下的包管理器。 webpack: 它主要用途是通过CommonJS 的语法把所有浏览器端需要发布的静态资源作相应的准备&#xff0c;比如资源的合并和打包。 vue-cli: 用户生成Vu…

jmeter集合点使用方法:Synchronizing Timer

LR中集合点可以设置多个虚拟用户等待到一个点&#xff0c;同时触发一个事务,以达到模拟真实环境下多个用户同时操作,实现性能测试的最终目的。 jmeter中使用Synchronizing Timer实现Lr中集合点的功能&#xff0c;模拟多用户并发测试&#xff0c;即多个线程在同一时刻并发请求。…

微信模版消息 errmsg: 'invalid weapp pagepath hint: [OtU1OA0868a394]

我是想在公众号的模版消息中&#xff0c;点击直接跳转到体验版小程序的页面中&#xff0c;使用了官方的写法&#xff1a; miniprogram: {// 小程序appidappid: config.wapp.appid,// 打开页面pagepath: "pages/home/ad" },直接报错了&#xff0c;其实需要把pagepath…

前端学习(2628):node.js中LTS和Current的区别

2016 年 10 月 18 日&#xff0c;Node.js v6 LTS (Boron) 发布&#xff0c;这也是 Node.js 启用 LTS 发布计划以来&#xff0c;第一次同时迎来两个 active LTS(v4 与 v6&#xff09;。这系列文章将讲述 Node.js v6 LTS 带来的一系列变化&#xff0c;本篇主要围绕 LTS 展开。如果…

谷歌chrome模拟手机浏览网页:iPhone/Android

部分网站通过Http头中的User-Agent判断浏览器类型&#xff0c;如果是手机&#xff0c;显示手机版页面内容&#xff0c;如果是普通PC浏览器&#xff0c;显示电脑版网页内容。 因此谷歌Chrome浏览器&#xff0c;可以通过修改User-Agent和页面分辨率模拟手机浏览器访问网页。如iPh…

前端学习(2629):npm安装成功配置

修改目录 修改镜像 输入命令npm config set registryhttp://registry.npm.taobao.org 配置镜像站 配置成功 测试镜像行吗