C#算法-深度优先寻路DeepFirst(控制台打印实现)

主要的思路:

1、要有一个既定的规则,严格按照规则进行搜索,比如:上 下 左 右;

2、需要用到stack容器(new一个叫做openList的stack容器),因为stack具有先进后出的特性;

3、每一个节点都有“位置 (postion) 、在地图上的类型(type) 还有是否已经被探索过(isOpen) ”三种属性;

过程:

每次探路过后,把经过的节点都压入栈中,每压入一个节点,把这个节点的属性改为已经被探索过;

然后把栈顶的节点设为当前节点继续往下探路,如果遇到岔路口则按照 “上 下 左 右” 的搜索规则来进行压栈;

找到第一个能走的方向就继续前进,如果遇到死路则从栈顶开始弹栈,一直返回到上一个岔路口,继续进行下一个方向的探索;

如果探索到了终点,也一样开始往外弹栈,每弹出一个节点,用另一stack容器接收,最终从stack容器挨个取出节点,打印出一条从起点到终点的路。

上代码:(可以直接全选复制使用)

using System;
using System.Collections.Generic;
using System.Threading;

namespace DeepFirst
{
    class Program
    {
        static void Main(string[] args)
        {
            DeepFindPath fp = new DeepFindPath();//创建一个对象

            fp.PrintMap();
            fp.Init();

            Pos start = new Pos() { x = 1, y = 1 };
            Pos end = new Pos() { x = 18, y = 18 };//给到起点和终点的坐标
            Stack<Pos> path = fp.PathFind(start, end);//传入起点终点坐标寻路


            while (path.Count > 0)
            {
                Pos pos = path.Pop();
                SetCursor(pos.x, pos.y);
                Console.WriteLine("○");

                Thread.Sleep(60);
                SetCursor(pos.x, pos.y);
            }//弹栈寻路

            SetCursor(30, 0);
        }

        static public void SetCursor(int x, int y)
        {
            Console.SetCursorPosition(2 * y, x);
        }
    }
    //Main函数调用,我这里写的不太好,主函数里内容有点多
    //应该用一些方法包装好,但是应该比较容易理解

    struct Pos { public int x, y; }

    class DeepFindPath
    {
        public int[,] mapData = new int[,]
        {
            {10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10},
            {10,0,0,0,0,0,0,0,0,0,10,0,10,0,10,0,0,0,0,10                },
            {10,10,10,10,10,0,10,10,10,0,10,0,10,0,10,0,10,10,0,10      },
            {10,0,0,0,10,0,0,0,10,0,10,0,10,0,10,0,10,0,0,10            },
            {10,0,10,0,10,0,10,0,10,0,10,0,10,0,0,0,10,0,10,10          },
            {10,0,10,0,10,0,10,0,10,0,10,0,10,10,10,10,10,0,10,10       },
            {10,0,10,0,0,0,10,0,0,0,10,0,0,0,0,0,0,0,0,10               },
            {10,0,10,10,10,10,10,0,10,0,10,0,10,10,10,10,0,10,10,10     },
            {10,0,10,0,0,0,0,0,10,0,10,0,10,0,0,10,0,0,0,10             },
            {10,0,10,10,10,10,10,10,10,0,0,0,10,0,0,10,10,10,10,10      },
            {10,0,0,0,0,0,0,0,10,10,10,10,10,10,0,10,0,10,0,10          },
            {10,0,10,10,10,10,10,0,0,0,0,0,0,10,0,10,0,10,0,10          },
            {10,0,10,0,0,0,10,10,10,10,10,10,0,10,0,10,0,10,0,10        },
            {10,0,10,0,10,0,10,0,0,0,0,10,0,10,0,10,0,0,0,10            },
            {10,0,10,0,10,0,10,0,10,10,10,10,0,10,0,10,0,10,0,10        },
            {10,0,10,0,10,0,0,0,10,0,0,0,0,0,0,10,0,10,0,10             },
            {10,0,0,0,10,0,10,0,10,0,10,10,10,10,10,10,0,10,0,10        },
            {10,0,10,10,10,10,10,0,10,0,10,0,0,0,0,0,0,10,0,10          },
            {10,0,10,0,0,0,0,0,10,0,0,0,10,10,10,10,0,10,0,10           },
            {10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10},
        };//地图数据

        int[,] dir = new int[,]
        {
            {-1,0 },//上
            { 1,0 },//下
            {0,-1 },//左
            { 0,1 },//右
        };//探路的规则,上下左右,用二维数组装坐标


        public void PrintMap()
        {
            for (int i = 0; i < mapData.GetLength(0); i++)
            {
                for (int j = 0; j < mapData.GetLength(1); j++)
                {
                    switch (mapData[i, j])
                    {
                        case 0:
                            Console.Write("  ");
                            break;
                        case 10:
                            Console.Write("■");
                            break;
                        default:
                            Console.WriteLine("没有{0}这种类型", mapData[i, j]);
                            break;
                    }
                }
                Console.WriteLine();
            }
        }//打印地图的方法


        class Tile
        {
            public Pos position;
            public bool isOpen;
            public int type;

            public Tile(int x, int y, int mapDate)
            {
                isOpen = false;
                position.x = x;
                position.y = y;
                type = mapDate;
            }
        }//内部类,定义节点

        Tile[,] tiles;//Tile数组接收节点

        public void Init()
        {
            if (tiles == null)
            {
                tiles = new Tile[mapData.GetLength(0), mapData.GetLength(1)];
            }

            for (int i = 0; i < mapData.GetLength(0); i++)
            {
                for (int j = 0; j < mapData.GetLength(1); j++)
                {
                    tiles[i, j] = new Tile(i, j, mapData[i, j]);
                }
            }
        }//给每一个节点附上属性,Pos(坐标)、
         //type(也就是地图上的类型,初始化的时候已经设定mapeData=type)

        Stack<Tile> openList = new Stack<Tile>();
        //设一个 Stack 容器,取名为 openList,用来装探索过的tile

        public Stack<Pos> PathFind(Pos start, Pos end)
        {
            openList.Clear();//首相清空openList
            
            PushTile(tiles[start.x, start.y]);
            //1、起点压栈
            //这里还缺一个起点就是终点的判断,我没有加上

            Stack<Pos> path = new Stack<Pos>();
            //再设一个 stack容器,接收弹出来的节点,返回一个路径 Path 

            while (openList.Count > 0) //当openList不为空是开始探路的过程
            {
                Thread.Sleep(100);//给一个延迟

                Tile curTile = openList.Peek();
                //设一个当前节点,为openList的第一个节点,第一次循环进来的时候即为起点

                bool canMove = false;//定一个是否能移动的布尔值备用

                for (int i = 0; i < dir.GetLength(0); i++)//因为有四个方向,循环四次探路
                {
                    Tile tile = tiles[curTile.position.x + dir[i, 0], curTile.position.y + dir[i, 1]];
                    //按上下左右探索,是否能移动在下面判断
                    if (tile.type != 10 && tile.isOpen == false /* && 边界条件(若地图是开放的)*/)
                    {
                        if (tile.position.Equals(end))//终点判断
                        {
                            PushTile(tile);
                            while (openList.Count > 0)
                            {
                                path.Push(openList.Pop().position);
                            }
                            return path;
                        }//如果是终点,开始弹栈,同时把弹出的节点压进 Path 里返回出来

                        else
                        {
                            canMove = true;
                            PushTile(tile);
                            break;
                        }
                         //如果四个方向都探索过了,没有找到终点
                         //把canMove标记为true,再把节点压进栈里
                         //因为如果遇到岔路口的话,找到一个方向能走就不再探索其他方向                                                   
                         //直接往能走的方向继续走了,所以这里要跳出循环
                         //PushTile 方法在下面,这里还要把节点的isOpen属性设为true
                    }
                }
                if (canMove)
                {
                    continue;
                }
                 //为了外层大循环继续,探索下一个节点,
                 //这里要 continue 一下,继续下个节点的探索
                else
                {
                    Tile popedTile = openList.Pop();
                    Program.SetCursor(popedTile.position.x, popedTile.position.y);
                    Console.WriteLine("  ");
                }
                 //如果一个节点的四个方向都已经探索过了,
                 //都不能走,那么会来到这个 else 的判断,开始弹栈
                 //把已经被弹出栈的节点记录一下,打上空格清除路径,相当于一路返回,
                 //一直返回到上一个岔路口,进入另一个方向
                 //因为岔路口的另一个方向的 isOpen属性 还是 false 所以会往另一个方向继续探路
            }
            return path;
        }

        void PushTile(Tile tile)
        {
            openList.Push(tile);
            tile.isOpen = true;
            Program.SetCursor(tile.position.x, tile.position.y);
            Console.WriteLine("□");
        }
    }
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值