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

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

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

思维逻辑如下:
从入口节点进去,节点前后左右的节点,只要是能通过就一直走,如果到达某个节点,节点前后左右节点已经没有格子可走了,就往回退到一个能向其他方向(已经走过的节点排除掉)的节点,继续向前走(走之前没走过的方向),最终一定能走到出口

中心思想是利用了算法中常用的:回溯函数剪枝方法

逻辑图
在这里插入图片描述

下面是使用递归和迭代两种方式实现的迷宫寻路算法
创建一个二维数组 int[][] grid; 作为迷宫数据
grid[x][y] == 0 或者 出界的节点 视为路(可走)
grid[x][y] == 1 视为障碍物(不可走)

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
{
    /// <summary>
    /// 递归实现
    /// </summary>
    RECURSION = 1,

    /// <summary>
    /// 迭代实现
    /// </summary>
    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);
    }
}

寻路结果动画展示
在这里插入图片描述
Unity项目工程下载地址迷宫项目所在路径 PathfindingAlgorithm\Labyrinth如果没有使用过Unity的看上边 C# 代码逻辑即可,Unity项目只是辅助展示。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值