DFS是被广泛运用的搜索算法,它属于一种盲目搜索,定义如下:
1、起始访问的顶点是指定的;
2、若当前访问的顶点的邻接顶点有未被访问的,则任选一个访问;反之,退回到最近访问过的顶点;直到与起始顶点相通的全部顶点都访问完毕;
3、若此时图中尚有顶点未被访问,则再选其中一个顶点作为起始顶点并访问之,转 2; 反之,遍历结束。
大多数时候我们用dfs时会采用递归的方式,但是非递归的方式更加直观,思路更清晰,更便于我们去理解”深度“的思想。这篇文章主要介绍如何用unity引擎来可视化模拟非递归的dfs算法。为了方便理解,给出一个经典的红黑格题目,要求如下:
有一个长方形的房间,房间里的地面上布满了正方形的瓷砖,瓷砖要么是红色的,要么是黑色的。一个人站在其中一块黑色的瓷砖上,他可以向四周的瓷砖上移动,但是不能移动到红色的瓷砖上,只能在黑色的瓷砖上移动,用深度优先搜索的思想模拟该过程。
输入数据有三个,分别是房间的长和宽,以及一个字符串。字符串含有房间中砖块的颜色信息,例如“#”表示黑色瓷砖,“*”表示红色瓷砖,“@”表示该位置的黑色瓷砖,那么字符串可以是
"***#**######@#***##**#***##***#***#****##*###*#**"
(注意,长和宽的乘积应该和字符串字符数相同。)
脚本代码如下(c#):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
using System;
using UnityEditor;
public class NewBehaviourScript : MonoBehaviour
{
public GameObject redcube, blackcube, yellowcube, origncube, Ethan;
public string Str1;
public int weight, height;
private int k = 0;
public char[,] map;
public int[] path;//记录走过的坐标
private int[] dx = { 1, -1, 0, 0 }, dy = { 0, 0, 1, -1 };
public int[,] visited;//记录每一块砖走过的次数
private int cnt = 0, row = 0, cnt2 = 0;
private int sx = 0, sy = 0;
public float time = 0;
private bool moved = false, isbacking = false, once = false;
private float deltaposxy = 4.2f, deltaposz = 0.27f;
void Start()
{
//初始化map 生成地图
Transform temp = transform;
temp.position = new Vector3(orign.transform.position.x, 0, orign.transform.position.z);
visited = new int[weight, height];
map = new char[weight, height];
path = new int[weight * height * 100];
for (int j = 0; j < height; j++)
{
for (int i = 0; i < weight; i++)
{
if (Str1[k] == '*')
{
map[i, j] = Str1[k];
temp.position = new Vector3(orign.transform.position.x + i * deltaposxy, 0, orign.transform.position.z + j * deltaposxy);
GameObject ared = Instantiate(red, temp.position, temp.rotation);
k++;
continue;
}
if (Str1[k] == '#')
{
map[i, j] = Str1[k];
temp.position = new Vector3(orign.transform.position.x + i * deltaposxy, 0, orign.transform.position.z + j * deltaposxy);
GameObject ablack = Instantiate(black, temp.position, temp.rotation);
k++;
continue;
}
if (Str1[k] == '@')
{
map[i, j] = Str1[k];
sx = i; sy = j;
temp.position = new Vector3(orign.transform.position.x + i * deltaposxy, 0, orign.transform.position.z + j * deltaposxy);
GameObject ayellow = Instantiate(yellow, temp.position, temp.rotation);
k++;
continue;
}
}
}
k = 0;
gameObject.transform.position = new Vector3(orign.transform.position.x + sx * deltaposxy, deltaposz, orign.transform.position.z + sy * deltaposxy);
dfs();
}
private int j = 0;
void Update()
{
time++;
if (time % 30 == 0)
{
if (cnt2 > 0)
{
Transform temp = transform;
temp.position = new Vector3(orign.transform.position.x + path[j] * deltaposxy, deltaposz, orign.transform.position.z + path[j + 1] * deltaposxy);
GameObject aethan = Instantiate(Ethan, temp.position, temp.rotation);
j += 2;
Destroy(aethan, 0.35f);
cnt2--;
}
}
}
void dfs()
{
visited[sx, sy] = 1;//标记1表示Ethan走过此砖一次
cnt++;
while (true)
{
for (int i = 0; i < 4; i++)
{
int nx = sx + dx[i], ny = sy + dy[i];//前后左右选择一个方向判断
if (nx >= 0 && nx < weight && ny >= 0 && ny < height)
{
if (visited[nx, ny] == 0 && map[nx, ny] != '*')//找到了某一方向有从未走过的砖,前进
{
path[row] = nx; row++;
path[row] = ny; row++;
moved = true;
//在返回的途中发现没走过的砖,将现在脚下拐弯的砖标记为1(原本应该标记为2),因为后面一定还会至少再经过一次
if (isbacking == true) visited[sx, sy] = 1; //
isbacking = false;
sx = nx; sy = ny;
visited[sx, sy] = 1;//将走过一次的砖标记为1
cnt++;
cnt2++;
time = 0;
break;
}
}
}
if (moved == true)
{
moved = false;
continue;
}
//前后左右都不存在没走过的砖,此时找走过一次的砖
for (int i = 0; i < 4; i++)
{
int nx = sx + dx[i], ny = sy + dy[i];//前后左右选择一个方向判断
if (nx >= 0 && nx < weight && ny >= 0 && ny < height)
{
if (visited[nx, ny] == 1 && map[nx, ny] != '*')//找到了某一方向有走过一次的砖,前进
{
path[row] = nx; row++;
path[row] = ny; row++;
moved = true;
sx = nx; sy = ny;
visited[sx, sy] = 2;//将走过两次的砖标记为2
isbacking = true;
time = 0;
cnt2++;
break;
}
}
}
if (moved == true)
{
moved = false;
continue;
}
Debug.Log(cnt);
break;
}
}
}
将此脚本挂在一个空物体上,并在unity中给脚本的每一个数据成员赋上初始值,点击开始就可以开到模拟出的dfs算法思路。效果如下:
扫描二维码关注公众号,回复:
1796298 查看本文章
谢谢观看:)