逃离大迷宫:寻路、回溯算法

逃离大迷宫:寻路、回溯算法

在一个 N * M 的网格中,每个格子的坐标为(x, y),从一个entry(迷宫入口) 开始到所有exit(迷宫出口可能有多个)的路径

创建一个二维数组 int[][] grid; 作为迷宫数据
grid[x][y] == 0 或者 出界的节点 视为路(可走)
grid[x][y] == 1 视为障碍物(不可走)

思路:利用回溯算法思想和剪枝函数来完成路径搜索
1.初始化路径:路径中节点个数为0,从入口格子节点开始,加入到路径中,并记录入口节点已经加入过路径中了。

2.获取路径中最后一个加入的节点X(该节点继续保存在路径中),
--------如果节点X是出口则成功搜索到一条路径了。将路径保存。
----------------如果只要求找到一条路径就算成功,则退出。
----------------如果要求找到所有能走出的路径,则继续执行下面逻辑
--------遍历节点X的上A、下B、左C、右D四个节点,不可走的节点或者之前加入过路径中的节点跳过(此为剪枝函数)
----------------如果节点可走,则将节点加入到路径中,并记录该节点已经加入过路径,GoTo 到 2 继续深度优先遍历
--------如果节点X的上、下、左、右 四个节点都没有可走的节点或者之前都添加到路径中,则从节点X经过无法到达出口,则应该回溯到节点X的上一个节点继续查找(此为回溯),如何回溯?回溯方法:将节点X从路径中移除,路径中最后一个就是节点X的上一个节点了。 GoTo到 2 继续深度优先遍历

3.到此从入口开始的所有能走通的路径都已经获取到了

上面 3 条逻辑用白话说就是:从入口进去,前后左右只要是能往前走就一直走(已经走过的路不能重复走),如果没有路可走了就往回退,直到退到一个能向其他方向(没做过的方向)走的点,继续向前走,最终一定能走到出口

下面是使用递归和迭代两种方式实现的迷宫寻路算法

using System.Collections.Generic;

public class Node
{
    public Node parent;
    public int index;
    public Node(int index)
    {
        this.index = index;
    }
    public Node(int index, Node parent)
    {
        this.index = index;
        this.parent = parent;
    }
}

// 寻路算法逻辑实现方式
public enum SearchType
{
    /// 
    /// 递归实现
    /// 
    RECURSION = 1,

    /// 
    /// 迭代实现
    /// 
    ITERATION = 2,
}

public class LabyrinthLogic
{
    private int[][] _grid;
    private int _entryRow = 0;
    private int _entryCol = 0;
    private HashSet<int> hash = new HashSet<int>();
    private List<List<int>> resultList = new List<List<int>>();
    int[][] dir = new int[][] {
        new int[]{-1, 0}, // 上
        new int[]{ 1, 0}, // 下
        new int[]{ 0,-1}, // 左
        new int[]{ 0, 1}  // 右
    };

    public List<List<int>> ResultList
    {
        get { return resultList; }
    }

    public bool FindPath(int[][] grid, int entryRow, int entryCol, SearchType type)
    {
        _grid = grid;
        _entryRow = entryRow;
        _entryCol = entryCol;
        hash.Clear();
        resultList.Clear();

        if (type == SearchType.RECURSION)
        {
            dfs(_grid.Length, _entryRow, _entryCol, new List<int>());
        }
        else if (type == SearchType.ITERATION)
        {
            dfs(_grid.Length, _entryRow, _entryCol);
        }
        return resultList.Count > 0;
    }

    #region 递归实现
    // 递归实现
    private void dfs(int n, int row, int col, List<int> list)
    {
        if (IsExit(row, col, n))
        {
            // 找到出口了,保存路径
            resultList.Add(new List<int>(list));
            return;
        }

        // 节点的上下左右节点中搜索
        for (int i = 0; i < dir.Length; ++i)
        {
            int newRow = row + dir[i][0];
            int newCol = col + dir[i][1];
            int index = newRow * n + newCol;
            // 剪枝函数
            // 已经搜索过的节点不重复添加(避免死循环)
            // 障碍物节点不能通过
            if (hash.Contains(index) || !Valid(newRow, newCol, n))
            {
                continue;
            }
            // 记录已经搜索过的节点
            hash.Add(index);
            // 将可以通过的节点保存
            list.Add(index);
            dfs(n, newRow, newCol, list);
            // 回溯
            list.RemoveAt(list.Count - 1);
        }
    }
    #endregion

    #region 迭代实现
    private void dfs(int n, int row, int col)
    {
        int index = row * n + col;
        Node node = new Node(index);
        Stack<Node> stack = new Stack<Node>();
        stack.Push(node);
        while (stack.Count > 0)
        {
            node = stack.Pop();
            index = node.index;
            row = index / n;
            col = index % n;
            if (IsExit(row, col, n))
            {
                List<int> list = new List<int>();
                while (null != node)
                {
                    list.Insert(0, node.index);
                    node = node.parent;
                }
                resultList.Add(list);
                continue;
            }

            for (int i = dir.Length - 1; i >= 0; --i)
            {
                int newRow = row + dir[i][0];
                int newCol = col + dir[i][1];
                index = newRow * n + newCol;
                if (!Valid(newRow, newCol, n) || hash.Contains(index))
                {
                    continue;
                }
                // 子节点设置父节点
                Node childNode = new Node(index, node);
                hash.Add(index);
                stack.Push(childNode);
            }
        }
    }
    #endregion

    // 是否有效节点,在地图范围内且节点值为0 的为有效节点
    private bool Valid(int row, int col, int n)
    {
        return row >= 0 && row < n && col >= 0 && col < n && _grid[row][col] == 0;
    }

    // 是否出口
    private bool IsExit(int row, int col, int n)
    {
        return !IsEntry(row, col) && (row <= 0 || row >= n - 1 || col <= 0 || col >= n - 1);
    }

    // 是否入口
    private bool IsEntry(int row, int col)
    {
        return (row == _entryRow && col == _entryCol);
    }
}

寻路结果动画展示
逃离大迷宫:寻路、回溯算法_第1张图片
Unity项目工程下载地址迷宫项目所在路径 PathfindingAlgorithm\Labyrinth如果没有使用过Unity的看上边 C# 代码逻辑即可,Unity项目只是辅助展示。

该代码是一个利用回溯算法是实现的没有做优化处理的Demo,对于小地图是没有问题的,当地图过大如 10000 X 10000 上面代码效率会极其低下,甚至会堆栈溢出崩溃

你可能感兴趣的:(寻路,逃离大迷宫,迷宫回溯算法,迷宫算法)