最近重新复习了一遍DFS,做下笔记
代码运行效果图:
代码:
#include <iostream>
using namespace std;
#include <list>
#include <stack>
#include <Windows.h>
int map[10][10]=
{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 2, 1, 0, 1, 0, 0, 0, 0, 1,
1, 0, 1, 0, 1, 0, 0, 0, 0, 1,
1, 0, 1, 0, 1, 1, 1, 1, 0, 1,
1, 0, 1, 0, 1, 1, 1, 1, 0, 1,
1, 0, 1, 1, 0, 0, 0, 1, 0, 1,
1, 0, 1, 0, 1, 1, 0, 1, 0, 1,
1, 0, 1, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 1, 0, 3, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};
void render()
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
switch (map[i][j])
{
case 0:
case 9:
cout << " ";
break;
case 1:
cout << "墙";
break;
case 2:
cout << "入";
break;
case 3:
cout << "出";
break;
default:
break;
}
}
cout << endl;
}
}
struct VEC2
{
int x;
int y;
};
// 寻找入口
VEC2 findEnter()
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
VEC2 p;
if (2 == map[i][j])
{
VEC2 p = {i,j};
return p;
}
}
}
VEC2 p;
return p;
}
// 创建路径链表,保存走过的路径
list<VEC2> _listPath;
// 创建分岔路口栈,用于保存分岔路口
stack<VEC2> _stackCorss;
// 寻找路径
void find(VEC2 pos)
{
//p保存下一个要走的节点
VEC2 next;
//记录周围四个点的空地的个数
int num = 0;
//每次从右开始,逆时针查找周围的四个位置,这个顺序决定了这个算法的走向,可以自由改变
for (int i = 1; i >= -1; i--)
{
for (int j = -1; j <=1; j++)
{
//不考虑斜着走的情况
if (abs(i) == abs(j))
continue;
//如果是空地
if (0 == map[i + pos.x][j + pos.y])
{
next.x = i + pos.x;
next.y = j + pos.y;
num++;
}
//如果是终点
else if (3 == map[i + pos.x][j + pos.y])
{
_listPath.push_back(pos);
return;
}
}
}
//当前位置周围只有一个空地,直接往下走
if (num == 1)
{
//标记此位置已经访问过
map[pos.x][pos.y] = 9;
_listPath.push_back(pos);
find(next);
}
//当前位置周围没有空地,回溯,取出栈顶元素,从这个元素的位置开始搜索
else if(num == 0)
{
//标记此位置已经访问过
map[pos.x][pos.y] = 9;
// 获取栈顶元素
pos = _stackCorss.top();
// 出栈
_stackCorss.pop();
// 删除链表中无效路径
for (list<VEC2>::iterator it = _listPath.begin();
it != _listPath.end();
it++)
{
VEC2 iPos = *it;
if (iPos.x == pos.x && iPos.y == pos.y)
{
_listPath.erase(it, _listPath.end());
break;
}
}
//从当前出栈的节点开始继续搜索可行路径
find(pos);
}
//当前位置周围有多个空地,即为分岔路口,入栈
//(注意只要周围空地>1,就每次把这个位置入栈,因为回溯时会出栈,所以只要周围还有空地,回溯时就又从这个位置开始搜索)
else
{
//将当前位置存放到路径链表中
_listPath.push_back(pos);
//将当前位置入栈
_stackCorss.push(pos);
//标记此位置已经访问过
map[pos.x][pos.y] = 9;
//从下一个位置开始搜索可行路径
find(next);
}
}
// 行走动画
void go()
{
VEC2 p = _listPath.front();
map[p.x][p.y] = 2;
system("CLS");
render();
}
int main()
{
render();
// 寻找起点
VEC2 startpos = findEnter();
// 寻找路径
find(startpos);
//_listPath.push_back(startpos);
while (_listPath.size() > 0)
{
go();
// 删除头
_listPath.pop_front();
//
Sleep(500);
}
return 0;
}
放上一张自己画的路径图,自己跟着程序走一遍就能理解的,DFS最主要用到了栈和递归,
当你走到一个位置有三种情况,
1、四周一直只有一个空地,那么只能往这个空地走;
2、这个位置的四周不止一个可以走的点,你就需要用栈来保存这样的分岔位置,这样如果你选择了其中一个位置往下走而走不下去的时候,就会回溯(也就是这个递归的回溯),从栈中取出栈顶元素,即分岔位置,然后从分岔位置开始尝试走其他没有走过的位置;
3、四周没有空地,从栈中取出栈顶元素,即上一个可以分岔的位置,然后从分岔位置开始尝试走其他没有走过的位置。注意栈可能为空,那就说明你一开始走就是四周都走不了。