中國礦業大學信控學院算法
1、 問題描述數組
問題中迷宮可用方陣[m,n]表示,0表示能經過,1表示不能經過。若要從從左上角[1,1]進入迷宮,設計算法,尋求一條從右下角 [m,n] 出去的路徑。咱們用遞增的數來表明尋找出口方向與步數,用-2來表明尋找過程當中找錯的路徑。數據結構
2、 需求分析函數
須要先建立一個迷宮,在開始後就開始搜尋,當一個點周圍有0點(改點並非以搜尋過的點),那麼到這裏繼續往下搜,若是搜到盡頭那麼就要倒回去,在搜尋倒回去的周圍還有爲搜尋過得0點,所以須要一個存儲算法,要知足後入先出,那麼就是棧,利用棧就能夠知足需求。測試
3、 算法分析spa
1. 首先先定義棧,本問題中,不須要在中間插入與彈出,插入與彈出操做均在棧頭,所以咱們利用順序棧。設計
而且該棧與實驗課的棧不一樣,由於要知足實際需求,因此咱們須要定義步數以及改點所在的位置以及臨近路徑的位置在咱們棧中,具體操做以下。code
1 //記錄通道塊在迷宮矩陣當中的橫、縱座標
2 struct Position { 3 int x; 4 int y; 5 }; 6
7 //放入棧當中的通道塊元素
8 struct SElement { 9 int ord;//記錄步數
10 Position p;//記錄位置
11 int di;//記錄下一次測試這一路徑的臨近路徑的位置
12 }; 13
14 struct MyStack { 15 SElement* base; 16 SElement* top; 17 int stacksize; 18 };
2. 棧的一些列基礎操做,例如建立棧,判斷棧是否爲空,取棧頂元素,獲取棧長,入棧,出棧。上述操做相似學校實驗中基本操做,這裏不在敘述,詳見附錄中所有代碼。blog
3. 接下來建立迷宮,這裏迷宮利用數組來生成,x軸爲n,y軸爲mit
n爲18,m爲15。生成方法簡單,這裏不在敘述,詳見附錄中所有代碼。
4. 接下來定義如何走這個迷宮,考慮咱們用0做爲通道,1做爲牆壁,那麼搜尋一個點的上下左右0就是能夠經過,1就是不能經過。利用如下循環來完成目標:
步驟1:挪到0處的點而且把剛纔位置入棧,讓改點改成步數,讓步數加一,而後繼續搜索其上下左右(除去剛纔經過的點)若是有0,繼續執行步驟1。
步驟2:若是周圍的點無0(除去剛纔已經經過的點),讓改點改成-2,那麼就出棧,把路徑調到剛纔的那個點,而且把步數減一,而後執行步驟1,搜尋其上下左右(出去剛纔走錯的點以及入這個點以前的點)。
直至入棧點是終點那麼退出循環。
代碼以下:
1 do
2 { 3 if (Pass(curp)) 4 { 5 FootPrint(curp, curStep);//可走就在迷宮裏面留下足跡 6 //建立一個棧元素,存儲可行路徑的相關值
7 SElement e; 8 e.di = 1;// 下一個路塊爲這一個路塊的右邊的路塊
9 e.ord = curStep; 10 e.p.x = curp.x; 11 e.p.y = curp.y; 12 Push(&path, e);//將路徑塊入棧
13 if (curp.x == m - 2 && curp.y == n - 2) break; //若是被壓入的路徑塊到了迷宮的終點就退出循環 14 //找到下一個被試塊
15 curp = NextPosition(curp, 1);//找到前一個被試塊東面的路徑塊做爲被試塊
16 curStep++;//被探索的步數加一
17 } 18 else//若是當前被試路徑不可以經過的話
19 { 20 if (!IsStackEmpty(&path)) 21 { 22 SElement e; 23 Pop(&path, &e); 24 curStep--; 25 //將這一段全部的周圍路徑都已經被測試過的路徑從棧中清除
26 while (e.di == 4 && !IsStackEmpty(&path)) { 27 MarkPrint(e.p); 28 Pop(&path, &e); 29 curStep--; 30 } 31 //若是當前棧頂還有未被測試的路徑就測試剩餘的周圍路徑
32 if (e.di<4) 33 { 34 curp = NextPosition(e.p, e.di + 1); 35 e.di++; 36 curStep++; 37 Push(&path, e); 38 } 39 } 40 } 41 } while (!IsStackEmpty(&path));
5. 在定義上述代碼中如何去搜尋,按照從右到下再到左再到上的方法搜尋。代碼以下:
1 //按順時針方向從右開始尋找矩陣當中某一個位置的臨近位置
2 Position NextPosition(Position now, int direction) 3 { 4 Position next; 5 int x = now.x; 6 int y = now.y; 7 switch (direction) 8 { 9 //右
10 case 1: { 11 next.x = x; 12 next.y = y + 1; 13 break; 14 } 15 //下
16 case 2: { 17 next.x = x + 1; 18 next.y = y; 19 break; 20 } 21 //左
22 case 3: { 23 next.x = x; 24 next.y = y - 1; 25 break; 26 } 27 //上
28 case 4:{ 29 next.x = x - 1; 30 next.y = y; 31 break; 32 } 33 default:break; 34 } 35 return next; 36}
6. 定義是否能夠經過函數,是就返回ture不是就返回false。代碼以下:
1 //輔助函數考察當前路徑可否經過
2 bool Pass(Position posi) 3 { 4 //只有路徑所在位置的數字爲0的是能夠走的
5 if (Maze[posi.x][posi.y] == 0) 6 { 7 return true; 8 } 9 return false; 10 }
7. 定義是入棧和出棧時如何修改迷宮數組中的值。代碼以下:
1 //改變改點爲步驟數
2 void FootPrint(Position p, int step) 3 { 4 Maze[p.x][p.y] = step; 5 } 6
7 //路徑不可走的話就留下-2的標記
8 void MarkPrint(Position p) 9 { 10 Maze[p.x][p.y] = -2; 11}
8. 定義打印迷宮函數,這個就是遍歷整個數組,過程簡單這裏不在敘述,見附錄中所有代碼。
4、 結論
編輯ing...
5、 參考文獻
[1] 嚴蔚敏,吳偉民——《數據結構》清華大學出版社2004.2.1
6、 附錄
完整代碼以下:
1 #include <stdio.h> 2 #include <malloc.h> 3 #define STACK_INIT_SIZE 100 4 #define STACKINCREMENT 10 5 6 //記錄通道塊在迷宮矩陣當中的橫、縱座標 7 struct Position { 8 int x; 9 int y; 10 }; 11 12 //放入棧當中的通道塊元素 13 struct SElement { 14 int ord;//記錄步數 15 Position p;//記錄位置 16 int di;//記錄下一次測試這一路徑的臨近路徑的位置 17 }; 18 19 struct MyStack { 20 SElement* base; 21 SElement* top; 22 int stacksize; 23 }; 24 25 //建立一個棧若是建立成功則返回1,不然就返回0 26 int InitStack(MyStack* s) 27 { 28 s->base = (SElement*)malloc(STACK_INIT_SIZE * sizeof(SElement));//爲棧分配初始空間 29 if (!s->base) return 0; 30 s->top = s->base;//設定爲空棧 31 s->stacksize = STACK_INIT_SIZE; 32 return 1; 33 } 34 35 //判斷棧是否爲空,若是是空的就返回0,不然就返回1 36 int IsStackEmpty(MyStack* s) 37 { 38 if (s->top == s->base) return true; 39 return false; 40 } 41 42 //獲取棧頂元素,若是棧爲空就返回0 不然就返回1 43 int GetTop(MyStack* s, SElement* e) 44 { 45 if (IsStackEmpty(s)) return 0; 46 e = s->top - 1; 47 return 1; 48 } 49 50 //獲取棧的長度,而且經過程序返回 51 int StackLength(MyStack* s) 52 { 53 return s->top - s->base; 54 } 55 56 //插入元素e爲新的棧頂元素,插入成功則返回1,不然返回0 57 int Push(MyStack* s, SElement e) 58 { 59 if (s->top - s->base >= STACK_INIT_SIZE) 60 { 61 s->base = (SElement*)realloc(s->base, (s->stacksize + STACKINCREMENT) * sizeof(SElement)); 62 if (!s->base) return 0; 63 s->top = s->base + s->stacksize; 64 s->stacksize += STACKINCREMENT; 65 } 66 *(s->top) = e; 67 s->top++; 68 return 1; 69 } 70 71 //彈出棧頂元素賦值給e彈出成功返回1,彈出失敗返回0 72 int Pop(MyStack* s, SElement* e) 73 { 74 if (IsStackEmpty(s)) return 0; 75 *e = *(s->top - 1); 76 s->top--; 77 return 1; 78 } 79 80 //定義牆元素爲1 可走路徑爲0 已知路徑爲curStep 不可以經過的路徑爲-2 81 #define m 15 82 #define n 18 83 int Maze[m][n] = { { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }, 84 { 1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1 }, 85 { 1,0,1,1,1,0,1,0,0,1,0,1,1,1,1,1,0,1 }, 86 { 1,0,1,1,1,0,1,0,1,1,0,1,0,1,1,0,0,1 }, 87 { 1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,0,1,1 }, 88 { 1,1,1,1,0,0,1,0,1,1,0,1,1,1,1,0,1,1 }, 89 { 1,1,1,0,0,1,1,0,1,1,0,0,0,1,1,1,1,1 }, 90 { 1,1,1,0,1,1,1,0,1,1,1,1,0,1,1,0,0,1 }, 91 { 1,1,1,0,0,0,0,0,0,0,1,1,0,1,1,0,1,1 }, 92 { 1,0,0,1,1,1,1,1,1,0,1,1,0,1,0,0,1,1 }, 93 { 1,1,0,0,0,0,0,0,0,0,1,0,0,1,0,1,1,1 }, 94 { 1,0,0,1,0,1,0,1,0,0,1,1,0,0,0,1,1,1 }, 95 { 1,1,0,1,0,1,0,1,1,1,0,0,0,1,1,1,1,1 }, 96 { 1,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1 }, 97 { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 } }; 98 99 //輔助函數考察當前路徑可否經過 100 bool Pass(Position posi) 101 { 102 //只有路徑所在位置的數字爲0的是能夠走的 103 if (Maze[posi.x][posi.y] == 0) 104 { 105 return true; 106 } 107 return false; 108 } 109 110 //按順時針方向從右開始尋找矩陣當中某一個位置的臨近位置 111 Position NextPosition(Position now, int direction) 112 { 113 Position next; 114 int x = now.x; 115 int y = now.y; 116 switch (direction) 117 { 118 //右 119 case 1: { 120 next.x = x; 121 next.y = y + 1; 122 break; 123 } 124 //下 125 case 2: { 126 next.x = x + 1; 127 next.y = y; 128 break; 129 } 130 //左 131 case 3: { 132 next.x = x; 133 next.y = y - 1; 134 break; 135 } 136 //上 137 case 4: 138 { 139 next.x = x - 1; 140 next.y = y; 141 break; 142 } 143 default:break; 144 } 145 return next; 146 } 147 148 //改變改點爲步驟數 149 void FootPrint(Position p, int step) 150 { 151 Maze[p.x][p.y] = step; 152 } 153 154 //路徑不可走的話就留下-2的標記 155 void MarkPrint(Position p) 156 { 157 Maze[p.x][p.y] = -2; 158 } 159 160 //打印出迷宮矩陣 161 void Display_migong() 162 { 163 for (int i = 0; i<m; i++) 164 { 165 for (int j = 0; j<n; j++) 166 { 167 if (Maze[i][j]<0) 168 printf("%d ", Maze[i][j]); 169 else if (Maze[i][j]<10) 170 printf("%d ", Maze[i][j]); 171 else 172 printf("%d ", Maze[i][j]); 173 } 174 printf("\n"); 175 } 176 } 177 178 int main() 179 { 180 181 //迷宮程序主體 182 MyStack path;//記錄路徑的棧 183 InitStack(&path);//初始化路徑數組 184 Position curp;//當前被試位置 185 Display_migong(); 186 //初始化當前位置爲矩陣入口 187 curp.x = 1; 188 curp.y = 1; 189 int curStep = 1;//被探索的步數 190 do 191 { 192 if (Pass(curp)) 193 { 194 FootPrint(curp, curStep);//可走就在迷宮裏面留下足跡 195 //建立一個棧元素,存儲可行路徑的相關值,將這個元素存儲到棧當中 196 SElement e; 197 e.di = 1;//下一個路塊爲這一個路塊的右邊的路塊 198 e.ord = curStep; 199 e.p.x = curp.x; 200 e.p.y = curp.y; 201 Push(&path, e);//將路徑塊入棧 202 if (curp.x == m - 2 && curp.y == n - 2) break; //若是被壓入的路徑塊到了迷宮的終點就退出循環 203 curp = NextPosition(curp, 1);//找到前一個被試塊東面的路徑塊做爲被試塊 204 curStep++;//被探索的步數加一 205 } 206 else//若是當前被試路徑不可以經過的話 207 { 208 if (!IsStackEmpty(&path)) 209 { 210 SElement e; 211 Pop(&path, &e); 212 curStep--; 213 //將全部的周圍路徑都已經被測試過的路徑從棧中清除 214 while (e.di == 4 && !IsStackEmpty(&path)) { 215 MarkPrint(e.p); 216 Pop(&path, &e); 217 curStep--; 218 } 219 //若是當前棧頂還有未被測試的路徑就測試剩餘的周圍路徑 220 if (e.di<4) 221 { 222 curp = NextPosition(e.p, e.di + 1); 223 e.di++; 224 curStep++; 225 Push(&path, e); 226 } 227 } 228 } 229 } while (!IsStackEmpty(&path)); 230 printf("\n"); 231 //打印出結果迷宮矩陣 232 Display_migong(); 233 return 0; 234 }