1. 程式人生 > 實用技巧 >資料結構與演算法——棧 迷宮演算法 回溯法

資料結構與演算法——棧 迷宮演算法 回溯法

找迷宮通路需要使用回溯法,找迷宮通路是對回溯法的一個很好的應用,實現回溯的過程用到資料結構—

回溯法:對一個包括有很多個結點,每個結點有若干個搜尋分支的問題,把原問題分解為若干個子問題求解的 演算法;當搜尋到某個結點發現無法再繼續搜尋下去時,就讓搜尋過程回溯(回退)到該節點的前一個結點,繼續 搜尋該節點外的其他尚未搜尋的分支;如果發現該結點無法再搜尋下去,就讓搜尋過程回溯到這個結點的前一 結點繼續這樣的搜尋過程;這樣的搜尋過程一直進行到搜尋到問題的解或者搜尋完了全部可搜尋分支沒有解存 在為止。

程式碼實現

maze.h

 1 #pragma once
 2 #include<stdio.h>
 3
#include<stdlib.h> 4 5 #define MAXSIZE 100 //預先分配空間,這個數值根據實際需要預估確定 6 7 typedef struct _Position //迷宮座標 8 { 9 int _x; 10 int _y; 11 }Position; 12 13 typedef Position ElemType; 14 15 typedef struct _SqStack 16 { 17 ElemType *base; //
棧底指標 18 ElemType *top; //棧頂指標 19 }SqStack; 20 21 bool InitStack(SqStack &S) //構造一個空棧 S 22 { 23 S.base = new ElemType[MAXSIZE]; //為順序棧分配一個最大容量為 MAXSIZE 的空間 24 if (!S.base) //空間分配失敗 25 return false; 26 S.top=S.base; //
top 初始為 base,空棧 27 28 return true; 29 } 30 31 bool PushStack(SqStack &S, ElemType e) // 插入元素 e 為新的棧頂元素 32 { 33 if (S.top-S.base == MAXSIZE) //棧滿 34 return false; 35 36 *(S.top++) = e; //元素 e 壓入棧頂,然後棧頂指標加 1,等價於*S.top=e; 37 S.top++; 38 39 return true; 40 } 41 42 bool PopStack(SqStack &S, ElemType &e) //刪除 S 的棧頂元素,暫存在變數 e 中 43 { 44 if (S.base == S.top) //棧空 45 { 46 return false; 47 } 48 e = *(--S.top); //棧頂指標減 1,將棧頂元素賦給 e 49 50 return true; 51 } 52 53 ElemType* GetTop(SqStack &S) //返回 S 的棧頂元素,棧頂指標不變 54 { 55 if (S.top != S.base) //棧非空 56 { 57 return S.top - 1; //返回棧頂元素的值,棧頂指標不變 58 } 59 else 60 { 61 return NULL; 62 } 63 } 64 65 int GetSize(SqStack &S) //返回棧中元素個數 66 { 67 return (S.top-S.base); 68 } 69 70 bool IsEmpty(SqStack &S) //判斷棧是否為空 71 { 72 if (S.top == S.base) 73 { 74 return true; 75 } 76 else 77 { 78 return false; 79 } 80 } 81 82 void DestoryStack(SqStack &S) //銷燬棧 83 { 84 if(S.base) 85 { 86 free(S.base); 87 S.base = NULL; 88 S.top = NULL; 89 } 90 }

maze.cpp

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include "maze.h"
  5 #include <assert.h>
  6 
  7 #define ROW 6
  8 #define COL 6
  9 
 10 typedef struct _Maze
 11 {
 12     int map[ROW][COL];
 13 }Maze;
 14 
 15 void InitMaze(Maze* m, int map[ROW][COL])            //迷宮的初始化
 16 {
 17     for (int i = 0;i< ROW ;++i)
 18     {
 19         for (int j = 0; j < COL; ++j)
 20         {
 21             m->map[i][j] = map[i][j];
 22         }
 23     }
 24 }
 25 
 26 void PrintMaze(Maze* m)                                //列印迷宮
 27 {
 28     for (int i = 0; i < ROW; ++i)
 29     {
 30         for (int j = 0; j < COL; ++j)
 31         {
 32             printf("%d ",m->map[i][j]);
 33         }
 34         printf("\n");
 35     }
 36     printf("\n");
 37 }
 38 
 39 int IsValidEnter(Maze* m, Position cur)                //判斷是否是有效的入口
 40 {
 41     assert(m);
 42     if ((cur._x == 0 || cur._x == ROW - 1) || (cur._y == 0 || cur._y == COL - 1) && (m->map[cur._x][cur._y] == 1))
 43         return 1;
 44     else
 45         return 0;
 46 }
 47 
 48 int IsNextPass(Maze* m,Position cur, Position next) //判斷當前節點的下一個節點能否走通
 49 {
 50     assert(m);
 51     
 52     //判斷 next 節點是否為 cur 的下一節點
 53     if(((next._x == cur._x) && ((next._y == cur._y+1)||(next._y == cur._y-1))) ||((next._y == cur._y) && ( (next._x == cur._x+1)||(next._x == cur._x-1))))
 54     {
 55         //在同一行上並且相鄰
 56         //或在同一列上並且相鄰
 57         //判斷下一個節點是否在迷宮裡面
 58         if (((next._x >= 0 && next._x < ROW) || (next._y >= 0 && next._y < COL)) &&(m->map[next._x][next._y] == 1))
 59         {
 60             return 1;
 61         }
 62     }
 63     return 0;
 64 }
 65 
 66 int IsValidExit(Maze* m, Position cur,Position enter) //判斷當前節點是不是有效的迷宮出口
 67 {
 68     assert(m);
 69     //這裡首先得保證該節點不是入口點,其次只要它處在迷宮的邊界即可
 70     if ((cur._x != enter._x || cur._y != enter._y) && ((cur._x == 0 || cur._x == ROW - 1) || (cur._y == 0 || cur._y == COL - 1)))
 71         return 1;
 72     else
 73         return 0;
 74 }
 75 
 76 int PassMaze(Maze* m,Position enter,SqStack* s)                        //找迷宮通路
 77 {
 78     assert(m && IsValidEnter(m,enter) == 1);                        //對給的迷宮的入口進行合法性判斷
 79     Position cur = enter;
 80     Position next;
 81     PushStack(*s, cur);                                                //首先將迷宮的入口壓入棧中
 82     m->map[cur._x][cur._y] = 2;                                        //將入口值改為 2
 83     
 84     //PrintMaze(m);
 85     while (!IsEmpty(*s)) 
 86     {
 87         cur = *GetTop(*s);
 88         //printf("cur: %d %d\n",cur._x, cur._y);
 89         if (IsValidExit(m,cur,enter) == 1)                            //判斷當前位置是否出口
 90             return 1;
 91         
 92         //嘗試向左一步:看當前節點的左一個節點能不能走通
 93         next = cur;
 94         next._y = cur._y - 1;
 95         if (IsNextPass(m, cur, next) == 1)
 96         {
 97             PushStack(*s, next);
 98             m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1;
 99             //PrintMaze(m);
100             continue;
101         }
102         
103         //嘗試向上一步:看當前節點的上一個節點能不能走通
104         next = cur;
105         next._x = cur._x - 1;
106         if (IsNextPass(m,cur,next) == 1)                            //next 節點能夠走通時,將其壓入棧中
107         {
108             PushStack(*s,next);
109             m->map[next._x][next._y] = m->map[cur._x][cur._y]+1;    //將 next 節點的值等於 cur 節點的值加 1
110             //PrintMaze(m);
111             continue;
112         }
113         
114         //右:看當前節點的向右的一個節點能不能走通
115         next = cur;
116         next._y = cur._y + 1;
117         if (IsNextPass(m, cur,next) == 1)
118         {
119             PushStack(*s, next);
120             m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1;
121             //PrintMaze(m);
122             continue;
123         }
124         
125         //下:看當前節點的下一個節點能不能走通
126         next = cur;
127         next._x = cur._x + 1;
128         if (IsNextPass(m, cur,next) == 1)
129         {
130             PushStack(*s, next);
131             m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1;
132             //PrintMaze(m);
133             continue;
134         }
135         
136         //走到這裡說明當前節點的四個方向都走不通,進行回溯,看前一個節點未被遍歷的方向是否還能走通
137         Position tmp;
138         PopStack(*s, tmp);
139     }
140     return 0;
141 }
142 int main()
143 {
144      //用二維陣列描繪迷宮:1 代表通路,0 代表牆
145     int map[ROW][COL] = 
146     {
147         0,0,1,0,0,0,
148         0,0,1,1,1,0,
149         0,0,1,0,0,0,
150         0,1,1,1,1,0,
151         0,0,1,0,1,0,
152         0,0,0,0,1,0
153     };
154     
155     Maze m;
156     Position enter;                            //迷宮入口
157     enter._x = 0;
158     enter._y = 2;
159     InitMaze(&m, map);
160     PrintMaze(&m);
161     
162     //system("pause");//exit(0);
163     SqStack s;                                //定義棧,儲存已走過的座標軌跡,便於回溯
164     InitStack(s);                            //棧的初始
165     int ret = PassMaze(&m,enter,&s);        //使用棧和回溯法解開迷宮
166     if(ret)
167     {
168         printf("恭喜你!終於找到了出口~\n");
169     }
170     else 
171     {
172         printf("不是我笨!實在沒有出口~\n");
173     }
174     
175     PrintMaze(&m);
176     system("pause");
177     
178     return 0;
179 }