深度優先搜索解決迷宮最短路徑問題

在之前的文章裏我們介紹了深度優先遍歷的思想,並且以數組全排列爲例子實現了算法編碼,本篇我們通過深度優先搜索解決迷宮最短路徑問題。用程序實現查詢從迷宮入口到終點的最短路徑,需要越過障礙物,並且不能超出迷宮界限。

算法圖解

假如有如下迷宮,我們需要找到從入口到終點的最短路徑,其中標記鎖的位置爲障礙物不可抵達。
迷宮最短路徑
我們先來看第一步可以達到的點有哪些?只有(1,2)和(2,1)。假如我們先往右走來到(2,1)這個點,下一步能到達的點只有(2,2)了,因爲(1,3)是障礙物,(1,1)已經走過了,也不能走。但是我們依然沒有到達終點,所以得繼續往下走,直至無路可走或到達終點爲止。注意!並不是到達終點就結束了,因爲到達終點只是嘗試了一條路,不一定就是最短的。剛纔在某些點我們有多個方向可供選擇,因此我們需要返回到這些點繼續嘗試往別的地方走,直到把所有可能都嘗試一遍,最後輸出最短路徑。
現在我們嘗試用深度優先搜索來實現這個方法dfs()。首先,dfs方法需要處理的內容包括:先檢查當前是否已到達終點處,如果沒有到達終點則尋找下一步可以走的地方。爲了達到目的,dfs函數需要三個參數,分別對應x,y座標以及當前步數step。定義如下:

        static void dfs(int x,int y,int step)
        {
            
        }

判斷當前是否到達終點很簡單,只需要判斷和終點座標是否相同

        static int p ;   //終點y
        static int q ;   //終點座標y
        static int min;     //當前最短路徑步數
        static void dfs(int x,int y,int step)
        {
            //判斷是否到達終點
            if (x==p&&y==q)
            {
                //更新最小值
                if (step<min)
                {
                    min = step;
                }
                return; //到達終點則返回
            }
        }

假如沒有到達終點,則需要找出下一步可以走的位置。因爲有四個方向可以走,我們定義一個方向數組next,如下:

        static int[][] next = new int[][]
        {
            new int[] { 0, 1 }, //向右
            new int[] { 1, 0 }, //向下
            new int[] { 0, -1 },//向左
            new int[] { -1, 0 } //向上
        };

深度優先搜索
通過這個方向數組,我們可以獲得下一步的座標,這裏我們用tx存儲下一步橫座標,ty存儲下一步縱座標。

   for (int i = 0; i < next.Length; i++)
   {
        //計算下一步的座標
        tx = x + next[i][0];
        ty = y + next[i][1];
   }

接下來對下一步的點(tx,ty)進行判斷,包括是否越界,是否爲障礙物,以及這個點是否已經走過,我們用book數組來記錄哪些點已經走過。
如果這個點滿足所有要求,就對這個點進行下一步的擴展,即dfs(step+1),一旦進行下一步嘗試就意味着步數已經增加了1。

            for (int i = 0; i < next.Length; i++)
            {
                //計算下一步的座標
                tx = x + next[i][0];
                ty = y + next[i][1];
                //判斷是否越界
                if (tx < 1 || tx > n || ty < 1 || ty > m)
                {
                    continue;
                }
                //判斷該點是否爲障礙物或者已經在路徑中
                if (a[tx][ty] == 0 && book[tx][ty] == 0)
                {
                    book[tx][ty] = 1;   //標記這個點已經走過
                    way.Add(string.Format("({0},{1})", tx, ty));    //記錄到路徑中,用於輸出
                    var index = way.Count - 1;
                    dfs(tx, ty, step + 1);  //開始嘗試下一步
                    book[tx][ty] = 0;   //嘗試結束後取消這個點的標記
                    way.RemoveAt(index);
                }
            }

我們來看一下完整的實現及調用:

    class Program
    {
        static void Main(string[] args)
        {
            int i, j, startx, starty;
            //初始化迷宮信息5*4
            n = 5;
            m = 4;
            for (i = 1; i <= n; i++)
            {
                a[i] = new int[5];
                book[i] = new int[5];
                for (j = 1; j <= m; j++)
                {
                    //(1,3),(3,3),(4,2),(5,4)處爲障礙物
                    if ((i == 1 && j == 3) || (i == 3 && j == 3) || (i == 4 && j == 2) || (i == 5 && j == 4))
                    {
                        a[i][j] = 1;
                    }
                    else
                    {
                        a[i][j] = 0;
                    }
                }
            }
            //終點爲(4,3)
            p = 4;
            q = 3;
            way.Add("(1,1)");
            book[1][1] = 1;
            dfs(1, 1, 0);   //起點爲(1,1),開始遍歷
            Console.WriteLine("最短步數爲:" + min);
            Console.ReadKey();
        }
        static int n, m, p, q, min = 99999;
        static int[][] a = new int[6][]; //記錄障礙物
        static int[][] book = new int[6][];//記錄走過的路徑
        static List<string> way = new List<string>();
        static void dfs(int x, int y, int step)
        {
            int[][] next = new int[][]
            {
                new int[] { 0, 1 }, //向右
                new int[] { 1, 0 }, //向下
                new int[] { 0, -1 },//向左
                new int[] { -1, 0 } //向上
            };
            int tx, ty;
            //判斷是否到達終點
            if (x == p && y == q)
            {
                //更新最小值
                if (step < min)
                {
                    min = step;
                }
                foreach (var item in way)
                {
                    Console.Write(item + " ");
                }
                Console.WriteLine("到達終點,步數{0}", step);
                return; //到達終點則返回
            }
            for (int i = 0; i < next.Length; i++)
            {
                //計算下一步的座標
                tx = x + next[i][0];
                ty = y + next[i][1];
                //判斷是否越界
                if (tx < 1 || tx > n || ty < 1 || ty > m)
                {
                    continue;
                }
                //判斷該點是否爲障礙物或者已經在路徑中
                if (a[tx][ty] == 0 && book[tx][ty] == 0)
                {
                    book[tx][ty] = 1;   //標記這個點已經走過
                    way.Add(string.Format("({0},{1})", tx, ty));    //記錄到路徑中,用於輸出
                    var index = way.Count - 1;
                    dfs(tx, ty, step + 1);  //開始嘗試下一步
                    book[tx][ty] = 0;   //嘗試結束後取消這個點的標記
                    way.RemoveAt(index);
                }
            }
            return;
        }

    }

我們在Main方法中按照上面的迷宮圖初始化了迷宮,然後從起點(1,1)調用深度優先搜索查找並打印所以通向終點的路徑,最後輸出最短步數,結果如下圖:
迷宮最短路徑
至此已經完成了迷宮最短路徑的搜索,如果對深度優先遍歷思想不太熟悉的可以參考我的上一篇文章:C#深度優先遍歷

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章