笔记来自数据结构与算法 (王桂林)
先定义一个栈(定义栈见笔记4,注意:本应用中在栈里存储的数据类型是 _Point (如下) 而非笔记4中的char,注意修改!)
typedef struct _Point
{
int _x;
int _y;
}Point;
深度优先搜索算法(英语:Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。链接:https://leetcode-cn.com/tag/depth-first-search/
来源:力扣(LeetCode)
比如要解决如上图的一个走迷宫问题(leetcode490),可以使用以下例法(基于栈)
Stack s;
int maze[MAXROW][MAXLINE]=
{
1,1,1,1,
0,0,0,1,
0,1,0,1,
1,1,0,0,
}
void visit(x,y)
{
Point p = {x,y};
push(&s,p) ;
}
// 0 路 1 墙 2 走过的路
int main()
{
Point sp = {1,0}, ep = {3,3}; //定义起点和终点
Stack s; //定义栈
initStack(&s); //初始化一个栈
push(&s,sp); //压入起始点
int flag =1;
while(!isStakEmpty(&s))
{
Point t;
t = pop(&s); // 弹出目前所在位置
maze[t._x][t._y]=2; //表示走过的路 (将目前所在点定义为已走到过)
system("cls");
displyMaz(); // 可自己写一个打印地图的函数来可视化地图变化
sleep(1);
//当所在点上下左右有空位置时,压入栈,当遇到岔路口时,
//此时应该为n压入,一弹出(朝该方向继续走),当一条路循环到死路时,
//此时栈中(因为沿着一条路走的时候每次一压一弹)栈顶应该是岔路的另
//一条路的起始点,继续尝试另一条路
if(t._x -1 >= 0 && maze[t._x-1][t._y]!=1 && maze[t._x-1][t._y]!=2)
//上
{
visit(t._x-1,t._y);
}
if(t._x +1 <= MAXROW && maze[t._x+1][t._y]!=1 && maze[t._x+1][t._y]!=2)
//下
{
visit(t._x+1,t._y);
}
if(t._y -1 >= 0 && maze[t._x][t._y-1]!=1 && maze[t._x][t._y-1]!=2)
//左
{
visit(t._x,t._y-1);
}
if(t._y+1 <= MAXLINE && maze[t._x][t._y+1]!=1 && maze[t._x][t._y+1]!=2)
//右
{
visit(t._x,t._y+1);
}
if(t._x == ep._x && t._y == ep._y)
{
flag = 0;
clearStack(&s);
break;
}
}
if(flag==0)
printf("find path");
else
printf("no path");
return 0;
}
上述方法标记了所有走过的路,以下方法能够最终打印出正确的路径,方法如下:
#include <stdio.h>
#include <mystack.h>
#include <string.h>
#define MAXROW 4
#define MAXLINE 4
Stack s;
Point prePoint[MAXROW][MAXLINE]; //☆建立一个坐标的二维数组来存储路
int maze[MAXROW][MAXLINE]=
{
1,1,1,1,
0,0,0,1,
0,1,0,1,
1,1,0,0,
}
void visit(int x,int y,Point t)
{
Point p = {x,y};
push(&s,p);
prePoint[x][y] = t; //☆ 下一个要走的位置的结点存了上一个位置的结点
}
void displyPrePoint()
{
for() //循环行 省略
{
for() // 循环列 省略
{
printf("(%2d,%2d)", prePoint[i][j];
}
putchar(10);
}
printf("==============\n");
}
// 0 路 1 墙 2 走过的路
int main()
{
Point sp = {1,0}, ep = {3,3}; //定义起点和终点
memset(prePoint,0xff, sizeof((Point)*MAXROW*MAXLINE)); ☆c初始化
//displyPrePoint(); //测试存储坐标系
Stack s; //定义栈
initStack(&s); //初始化一个栈
push(&s,sp); //压入起始点
int flag =1;
while(!isStakEmpty(&s))
{
Point t;
t = pop(&s); // 弹出目前所在位置
maze[t._x][t._y]=2; //表示走过的路 (将目前所在点定义为已走到过)
system("cls");
displyMaz(); // 可自己写一个打印地图的函数来可视化地图变化
sleep(1);
//当所在点上下左右有空位置时,压入栈,当遇到岔路口时,
//此时应该为n压入,一弹出(朝该方向继续走),当一条路循环到死路时,
//此时栈中(因为沿着一条路走的时候每次一压一弹)栈顶应该是岔路的另
//一条路的起始点,继续尝试另一条路
if(t._x -1 >= 0 && maze[t._x-1][t._y]!=1 && maze[t._x-1][t._y]!=2)
//上
{
visit(t._x-1,t._y,t);//☆加入t (t表示刚刚弹出来的元素) 弹出目前所在位置 //t._x_1,t._y表示下一个要走的位置
}
if(t._x +1 <= MAXROW && maze[t._x+1][t._y]!=1 && maze[t._x+1][t._y]!=2)
//下
{
visit(t._x+1,t._y,t);
}
if(t._y -1 >= 0 && maze[t._x][t._y-1]!=1 && maze[t._x][t._y-1]!=2)
//左
{
visit(t._x,t._y-1,t);
}
if(t._y+1 <= MAXLINE && maze[t._x][t._y+1]!=1 && maze[t._x][t._y+1]!=2)
//右
{
visit(t._x,t._y+1,t);
}
if(t._x == ep._x && t._y == ep._y)
{
flag = 0;
clearStack(&s);
break;
}
}
if(flag==0)
printf("find path\n");
else
printf("no path\n");
displyPrePoint(); //☆
Point t = ep;//☆
while(t._y != -1) //☆
{
printf("(%d,%d)",t._x,t._y);
t = prePoint[t._x][t._y];
}
return 0;
}
PrePoint里面存的是一个点的坐标,通过倒推,比如
PrePoint[3][3] =(3,2)
....
PrePoint[1][2] = (1,1)
PrePoint[1][1] = (1,0)
一步一步逆序找到路的连接点
我在嵌入式推箱子游戏设计中用到过类似的思路,参考地址:https://blog.youkuaiyun.com/qq_37286676/article/details/103213757