迷宮問題,另闢蹊徑,不用遞歸不用棧

迷宮的求解問題,通常的做法是採用窮舉法, 數據結構使用到棧,這也是目前基本所有的數據結構與算法類書籍給出的一般解法。

本文介紹另外一種算法求解, 不需要用到棧。

算法思想:
關於迷宮, 不失一般性, 沿着一個方向走,碰到牆壁可以回頭。
沿着一個方向一直走下去,如順時針,如果迷宮有解,那麼必定會走到出口,如果最後又返回到入口的方向,說明迷宮無解。
此算法不需要用到額外的數據結構,這是它的優點,只需要注意到下一方向的計算,以及探尋位置不通後的返回位置。缺點也很顯明,那就是同一位置可能會走過兩次。
算法的時間複雜度O(m*n),m、n表示迷宮的長寬, 空間複雜度O(1)。

源碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/**@brief   find maze path  
 *          10 * 10 的迷宮, 字符爲‘#’,表示不能通過,‘0’表示可以通過,走過的路徑用‘1’標記
 *          入口a[0][0],出口a[9][9],假定入口是通的
 * @return  1, 0
 */
int maze_path(char maze[10][10])
{
    int dir = 1;  //前進方向指示,1,2,3,4依次表示東、南、西、北方向,前進方向一走沿着東南西北的順序方向;初始方向爲東

    int tmp_i, tmp_j, i, j; /* i j 用來表示當前的位置,tmp_i, tmp_j用來探尋下一位置,如果不通不會走過去*/

    i = 0;
    j = 0;
    tmp_i = 0;
    tmp_j = 0;
    maze[0][0] = '1';   //標記入口是通的

    do
    {
        /* 根據探尋前進方向,求出探尋位置點 */
        switch (dir)
        {
            case 1:
                tmp_j++;
                break;

            case 2:
                tmp_i++;
                break;

            case 3:
                tmp_j--;
                break;

            case 4:
                tmp_i--;
                break;
        }

        if ((tmp_i>=0)&&(tmp_i<=9)&&(tmp_j>=0)&&(tmp_j<=9)&&('#' != maze[tmp_i][tmp_j])) //探尋位置不是牆
        {
            /* 位置通過,i j 走到探尋的tmp_i tmp_j位置,且標記爲字母'1' */
            i = tmp_i;
            j = tmp_j;
            maze[i][j] = '1';

            if (9==i && 9==j)
            {//走到了出口,返回
                return 1; 
            }

            /* 方向指示,如果是由上一個位置過來,則方向對應: 東西南北-> 西東北南 */
            if (dir >= 3)
            {
                dir -= 2;
            }
            else
            {
                dir += 2;
            }
        }
        else
        {//探尋位置是牆
            if (0==i && 0==j && 4==dir)  //又回到了入口,且探尋到北方向,說明已經探尋完畢,沒有路徑
            {
                return 0;
            }

            /* 探尋的位置回退,準備探尋下一方向 */
            tmp_i = i;
            tmp_j = j;
        }

        /* 方向指示到下一方向 */
        if (4 == dir) 
        {
            dir = 1;  //當前是北,下一方向指向東
        }
        else
        {
            dir++; 
        }   

    }while(1);
}

/* demo */
int main()
{
    char maze[10][10];
    int i = 0;
    int j = 0;

    srand(time(NULL)); //random seed

    do
    {
        /* 1 隨機生成迷宮 */
        memset(maze, '0', sizeof(maze));
        for (i=0; i<40; i++)
        {
            *((char*)((char*)maze+rand()%98+1)) = '#';   //a[0][1] 到 a[9][8],隨機置'#'迷宮牆,跳過入口a[0][0]出口a[9][9]
        }
        printf("maze:\n");
        for (i=0; i<10; i++)
        {
            for (j=0; j<10; j++)
            {
                printf("%c ", maze[i][j]);
            }
            printf("\n");
        }
        printf("\n");

        /* 2 找迷宮路徑 */
        if (maze_path(maze) == 1)
        {
            printf("find path:\n");
            for (i=0; i<10; i++)
            {
                for (j=0; j<10; j++)
                {
                    printf("%c ", maze[i][j]);
                }
                printf("\n");
            }
            printf("\n");
        }
        else
        {
            printf("no path:\n");
            for (i=0; i<10; i++)
            {
                for (j=0; j<10; j++)
                {
                    printf("%c ", maze[i][j]);
                }
                printf("\n");
            }
            printf("\n");
        }

        printf("Enter q to leave, the other key to continue...\n ");
    }while (getchar() != 'q');

    return 0;
}

測試結果:
這裏寫圖片描述

後記:
生活中越來越多的主題樂園都有迷宮,供遊客遊玩, 如果在限定時間內走出迷宮,有些地方還會提供獎勵。其實實際走迷宮, 不可能按照計算機遞歸的方式去窮舉,你不可能記住之前走過的那麼多步驟。假如按照本文的算法思想,走出迷宮是一件很輕而易舉的事:
一走沿着一個方向走, 比如遇到叉路就向右拐,遇到死衚衕就調頭。這麼說可能還是有人不明白,但簡單些, 從入口進去,右手扶着牆壁,然後一直往前走的過程中,保持右手始終可以接觸牆壁。
只要你走路速度夠快,能快速也走出迷宮,so easy。

發佈了37 篇原創文章 · 獲贊 13 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章