我們首先給出一個迷宮,它的規格是5 * 5,在這裡我使用int的二維數組表示迷宮,其中1表示障礙,0表示可以通行的道路,要求從(0,0)座標走到(4, 4)座標,並輸出走過的座標路徑。
int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0,};
①使用棧來實現DFS(深度優先搜尋)
首先,我們給出棧在迷宮中的的結構定義。
/* * x是迷宮中的橫座標 * y是迷宮中的縱座標 * direction表示在當前座標內要走的下一個方向 * next指標則指向棧中的下一個座標 */typedef struct LNode { int x; int y; int direction; struct LNode *next;}LNode;/* * top指標指向棧的頂部 * 不需要base指標 */typedef struct { LNode *top;}Stack;
接著,我們定義一些棧的基本函數操作:
/* * IntiStack() * 初始化一個棧,傳入一個Stack結構體的地址或結構指標 * top指標指向一個頭結點,該頭結點不包含座標但next指標指向棧的頂部座標結點 */void IntiStack(Stack *result){ result->top = (LNode *)malloc(sizeof(LNode)); result->top->next = NULL;}
/* * Push() * 將一個新的座標結點放入棧中,所使用的方法類似於表頭插入法 * 其中direction是現在的座標點下一步要走的方向 */ void Push(Stack *result, int x, int y, int direction){ LNode *topNode = (LNode *)malloc(sizeof(LNode)); topNode->x = x; topNode->y = y; topNode->direction = direction; topNode->next = result->top->next; result->top->next = topNode;}
/* * Pop() * 該函數用於走到迷宮的死路時向後退以及遍曆輸出結點 * 彈出top指標中next指標所指向的結點,也就是棧的頂部節點 * 類似於從表頭刪除一個節點,並返回該節點 */ LNode *Pop(Stack *result){ LNode *temp = result->top->next; if(temp != NULL){ result->top->next = temp->next; } temp->next = NULL; return temp;}
我們再給出main函數的定義
int main(void){ /* * MAX的值為5 * 定義一個二維數組 * 輸入迷宮的數值,並初始化一個棧 */ int maze[MAX][MAX]; int i, j; for(i = 0;i < MAX;i++){ for(j = 0;j < MAX;j++){ scanf("%d", &maze[i][j]); } } Stack result; IntiStack(&result); /* * 當處於一個迷宮結點的時候,我們應該分別檢測四個方向是否有路走 * 暫訂direction = 1, 2, 3, 4分別表示東,南,西,北 * flagDirection 則表示已經找到一個可行的方向,它是一個哨兵 * row, col表示當前結點的座標 * 首先將(0, 0)結點入棧,direction預設從1開始,它還將經過一次順時針遍 曆,所以它的東方向即使是障礙也沒關係 * 因為已經遍曆過(0, 0)這個點我們不會再走這個點所以把它堵上 * current指標指向棧的頂部節點 */ int flagDirect = 1; int row = 0; int col = 0; int direction = 1; Push(&result, row, col, direction); maze[row][col] = 1; LNode *current; /* * 設定了一個無限迴圈,跳出迴圈的條件是到達點(4, 4) * 如何順時針的遍曆四個方向。通過使用switch判斷可行的方向,當方向不可行的 時候direction加1測試下一個方向是否可行 * 但是當所有方向都不可行時證明該座標是迷宮中的一條死路需要退回去 * 這時通過Pop操作出棧退回到有路可走的座標點 * 之前把走過的座標點堵上就是為了不再走重複的路避免在死路裡迴圈,也方便出棧 */ while(1){ if(row == (MAX - 1) && col == (MAX - 1)){ break; } flagDirect = 1; current = result.top->next; row = current->x; col = current->y; while(flagDirect){ switch(current->direction){ case 1:if(col + 1 < MAX && maze[row][col + 1] == 0){ maze[row][col] = 1; col++; Push(&result, row, col, 1); flagDirect = 0; } else{ current->direction++; } break; case 2:if(row + 1 < MAX && maze[row + 1][col] == 0){ maze[row][col] = 1; row++; Push(&result, row, col, 1); flagDirect = 0; } else{ current->direction++; } break; case 3:if(col - 1 >= 0 && maze[row][col - 1] == 0){ maze[row][col] = 1; col--; Push(&result, row, col, 1); flagDirect = 0; } else{ current->direction++; } break; case 4:if(row - 1 >= 0 && maze[row - 1][col] == 0){ maze[row][col] = 1; row--; Push(&result, row, col, 1); flagDirect = 0; } else{ current->direction++; } break; default:Pop(&result); flagDirect = 0; break; } } } show(&result); return 0;}
最後當我們輸出棧的路徑的時候,我們是從終點倒回到起點出棧輸出的,這時,我們可以使用一個遞迴輸出起點到終點
void show(Stack *result){ LNode *temp; if(result->top->next != NULL){ temp = Pop(result); show(result); printf("(%d, %d)\n", temp->x, temp->y); }}
②使用隊列來實現BFS(廣度優先搜尋)
隊列在迷宮中的資料結構定義
/* * 該隊列為一個雙重鏈表, 通過prev指標向前索引,但該索引關係不符合循序關聯性, next指標符合循序關聯性 */ typedef struct QNode { int x; int y; struct QNode *prev; struct QNode *next;}QNode;typedef struct { QNode *front; QNode *rear;}Queue;
接著給出隊列基本函數操作定義:
/* * IntiQueue() * 初始化隊列,建立front指標的頭結點 * rear指標一開始的時候也指向頭結點 */ void IntiQueue(Queue *result){ result->front = (QNode *)malloc(sizeof(QNode)); result->front->prev = NULL; result->rear = result->front;}
/* * Push() * 將當前座標的結點入隊,其中prev指標表示該結點的上一個座標 * 表尾插入法就好了 */ void Push(Queue *result, int x, int y, QNode *prev){ QNode *temp = (QNode *)malloc(sizeof(QNode)); temp->x = x; temp->y = y; temp->prev = prev; temp->next = NULL; result->rear->next = temp; result->rear = temp;}
給出main函數定義
int main(void){ /* * MAX的值為5 * 定義一個二維數組 * 輸入迷宮的數值,並初始化一個隊列 */ int maze[MAX][MAX]; int i, j; for(i = 0;i < MAX;i++){ for(j = 0;j < MAX;j++){ scanf("%d", &maze[i][j]); } } Queue result; IntiQueue(&result); /* * row, col 為當前的橫縱座標 * (0, 0)的先前座標是隊列的頭結點,也就是result.front * find是一個哨兵,標誌著是否到達終點 * current指標指向當前的座標結點,並將當前座標節點周圍可走的座標入隊,如果 current為空白的時候則表明找不到終點,隊列的遍曆類似於樹中的層序遍曆 int row = 0, col = 0; Push(&result, row, col, result.front); maze[row][col] = 0; int direction = 1; int find = 0; QNode *current = result.front->next; /* * 使用順時針遍曆,能走的座標就入隊,並且為了防止重複的座標入隊,必須將走過 的路堵上 */ while(current != NULL && !find){ direction = 1; row = current->x; col = current->y; if(row == MAX - 1 && col == MAX - 1){ find = 1; break; } while(direction <= 4){ switch(direction){ case 1:if(col + 1 < MAX && maze[row][col + 1] == 0){ Push(&result, row, col + 1, current); maze[row][col + 1] = 1; } break; case 2:if(row + 1 < MAX && maze[row + 1][col] == 0){ Push(&result, row + 1, col, current); maze[row + 1][col] = 1; } break; case 3:if(col - 1 >= 0 && maze[row][col - 1] == 0){ Push(&result, row, col - 1, current); maze[row][col - 1] = 1; } break; case 4:if(row - 1 >= 0 && maze[row - 1][col] == 0){ Push(&result, row - 1, col, current); maze[row - 1][col] = 1; } break; } direction++; } current = current->next; } show(&result, current); return 0;}
show函數只要通過prev指標索引就能得到一條從起點走向終點的路徑,當然需要遞迴。
void show(Queue *result, QNode *current){ if(current->prev != NULL){ show(result, current->prev); printf("(%d, %d)\n", current->x, current->y); }}
該解法其實是poj 3984迷宮問題的解法,連結http://poj.org/problem?id=3984