1. 程式人生 > >資料結構之迷宮求解

資料結構之迷宮求解

//SeqStack.h
#pragma once

#include<stdio.h>
#include<stddef.h>
#include<windows.h>

#define MAZE_ROW 6  //行
#define MAZE_COL 6  //列


typedef struct Maze {
    int map[MAZE_ROW][MAZE_COL];
}Maze;

#define FOR_MAZE
#ifdef FOR_MAZE
typedef struct Point {
    int row;
    int col;
}Point;
typedef
Point SeqStackType; #else typedef char SeqStackType; #endif #define SeqStackMaxSize 1000 typedef struct SeqStack { SeqStackType data[SeqStackMaxSize]; size_t size; } SeqStack; void SeqStackInit(SeqStack* stack);//初始化 void SeqStackDestory(SeqStack* stack);//銷燬 void SeqStackPush(SeqStack* stack, SeqStackType value);//入棧
void SeqStackPop(SeqStack* stack);//出棧 int GetTop(SeqStack* stack, SeqStackType* value);//取棧頂元素 size_t SeqStackSize(SeqStack* stack); void SeqStackAssign(SeqStack* from, SeqStack* to); //賦值 #ifdef FOR_MAZE void SeqStackDebugPrintPoint(SeqStack* stack, const char* msg); #endif
//maze.c
//迷宮求解思路:深度優先
#include"SeqStack.h"
void SeqStackInit(SeqStack* stack) { if (stack == NULL) { return; } stack->size = 0; } void SeqStackDestory(SeqStack* stack) { if (stack == NULL) { return; } stack->size = 0; } void SeqStackPush(SeqStack* stack, SeqStackType value) { if (stack == NULL) { return; } if (stack->size >= SeqStackMaxSize) return; stack->data[stack->size++] = value; } void SeqStackPop(SeqStack* stack) { if (stack == 0) { return; } if (stack->size == 0) return; --stack->size; } int GetTop(SeqStack* stack, SeqStackType* value) { if (stack == NULL || value == NULL) { return 0; } if (stack->size == 0) return 0; *value = stack->data[stack->size - 1]; return 1; } size_t SeqStackSize(SeqStack* stack) { if (stack == NULL) { return 0; } return stack->size; } void SeqStackAssign(SeqStack* from, SeqStack* to) { //複製 if (from == NULL || to == NULL) { return; } to->size = from->size; size_t i = 0; for (; i < from->size; ++i) { to->data[i] = from->data[i]; } } #ifdef FOR_MAZE #include<stdio.h> void SeqStackDebugPrintPoint(SeqStack* stack, const char* msg) { if (stack == NULL) { printf("stack == NULL\n"); return; } printf("\n[%s]\n", msg); printf("[棧底]\n"); size_t i = 0; for (; i < stack->size; ++i) { printf("(%d, %d)\n", stack->data[i].row, stack->data[i].col); } printf("[棧頂]\n"); } #endif void MazeInit(Maze* maze) { if (maze == NULL) { return; } int map[MAZE_ROW][MAZE_COL] = { {0, 1, 0, 0, 0, 0}, {0, 1, 1, 1, 0, 0}, {0, 1, 0, 1, 1, 0}, {0, 1, 1, 0, 0, 0}, {0, 0, 1, 0, 0, 0}, {0, 0, 1, 0, 0, 0} }; size_t i = 0; for (; i < MAZE_ROW; ++i) { size_t j = 0; for (; j < MAZE_COL; ++j) { maze->map[i][j] = map[i][j]; } } } void MazeInitMultiExit(Maze* maze) { //多出口地圖,找到最短路徑 if (maze == NULL) { return; } int map[MAZE_ROW][MAZE_COL] = { { 0, 1, 0, 0, 0, 0 }, { 0, 1, 1, 1, 0, 0 }, { 0, 1, 0, 1, 1, 1 }, { 1, 1, 1, 0, 0, 0 }, { 0, 0, 1, 0, 0, 0 }, { 0, 0, 1, 0, 0, 0 } }; size_t i = 0; for (; i < MAZE_ROW; ++i) { size_t j = 0; for (; j < MAZE_COL; ++j) { maze->map[i][j] = map[i][j]; } } } void MazeInitMultiExitWithCycle(Maze* maze) { //多出口帶環的迷宮 if (maze == NULL) { return; } int map[MAZE_ROW][MAZE_COL] = { { 0, 1, 0, 0, 0, 0 }, { 0, 1, 1, 1, 0, 0 }, { 0, 1, 0, 1, 1, 1 }, { 1, 1, 1, 1, 0, 0 }, { 0, 0, 1, 0, 0, 0 }, { 0, 0, 1, 0, 0, 0 } }; size_t i = 0; for (; i < MAZE_ROW; ++i) { size_t j = 0; for (; j < MAZE_COL; ++j) { maze->map[i][j] = map[i][j]; } } } void MazePrint(Maze* maze) { if (maze == NULL) { return; } size_t i = 0; for (; i < MAZE_ROW; ++i) { size_t j = 0; for (; j < MAZE_COL; ++j) { printf("%2d ", maze->map[i][j]); } printf("\n"); } printf("\n"); } int CanStay(Maze* maze, Point cur) { if (maze == NULL) { return 0; } //1. 判定點在地圖上 if (cur.row < 0 || cur.row >= MAZE_ROW|| cur.col < 0 || cur.col >= MAZE_COL) { return 0; } //2. 當前點對應的值是1. if (maze->map[cur.row][cur.col] == 1) { return 1; } return 0; } void Mark(Maze* maze, Point cur) { if (maze == NULL) { return; } maze->map[cur.row][cur.col] = 2; } int IsExit(Point cur, Point entry) { //1. 當前點在地圖的邊緣上 //2. 當前點不是入口 if (cur.row == 0 || cur.col == 0 || cur.row == MAZE_ROW - 1 || cur.col == MAZE_COL - 1) { //在邊緣上 if (cur.row == entry.row && cur.col == entry.col) { //入口點 return 0; } return 1; } return 0; } void _HasPath(Maze* maze, Point cur, Point entry) { if (maze == NULL) { return; } //1. 判定當前點是否能落腳(點在地圖上且位置的值為1) if (!CanStay(maze, cur)) { return; } //2. 標記當前點是走過的點 Mark(maze, cur); //3. 判定當前點是否是出口(落在邊緣上且不是入口) if (IsExit(cur, entry)) { printf("找到了路徑!\n"); return; } //4. 按照順時針方向探測周圍的鄰接點(遞迴的呼叫HasPath) Point up = cur; up.row -= 1; _HasPath(maze, up, entry); Point right = cur; right.col += 1; _HasPath(maze, right, entry); Point down = cur; down.row += 1; _HasPath(maze, down, entry); Point left = cur; left.col -= 1; _HasPath(maze, left, entry); //5. 如果四個方向都探測過了,就直接返回 return; } //使用遞迴的方式來查詢迷宮上是否存在一條路徑 void HasPath(Maze* maze, Point entry) { if (maze == NULL) { return; } _HasPath(maze, entry, entry); } void HasPathByLoop(Maze* maze, Point entry) { if (maze == NULL) { return; } //1. 判定入口點是否能落腳,如果不能,表示非法輸入,直接返回 if (!CanStay(maze, entry)) { //入口點非法 return; } //2. 標記入口點走過了,把入口點入棧 Mark(maze, entry); SeqStack stack; SeqStackInit(&stack); SeqStackPush(&stack, entry); //3. 進入迴圈,取棧頂元素為當前點 Point cur; while (GetTop(&stack, &cur)) { //4. 判定當前點是否為出口(如果是出口,說明找到路了) if (IsExit(cur, entry)) { printf("找到了路徑!\n"); return; } //5. 按照一定順序判定當前點的四個鄰接點是否能落腳 //6. 如果某一個鄰接點能夠落腳,標記這個鄰接點, // 並且把鄰接點入棧,並且直接進入下一次迴圈 Point up = cur; up.row -= 1; if (CanStay(maze, up)) { Mark(maze, up); SeqStackPush(&stack, up); continue; } Point right = cur; right.col += 1; if (CanStay(maze, right)) { Mark(maze, right); SeqStackPush(&stack, right); continue; } Point down = cur; down.row += 1; if (CanStay(maze, down)) { Mark(maze, down); SeqStackPush(&stack, down); continue; } Point left = cur; left.col -= 1; if (CanStay(maze, left)) { Mark(maze, left); SeqStackPush(&stack, left); continue; } //7. 四個方向都探測完畢,把當前點出棧 SeqStackPop(&stack); } return; } void _GetShortPath(Maze* maze, Point cur, Point entry, SeqStack* cur_path, SeqStack* short_path) { //1. 判定當前點是否能落腳 if (!CanStay(maze, cur)) { return; } //2. 標記當前點,把當前點push到cur_path Mark(maze, cur); SeqStackPush(cur_path, cur); //3. 判定當前是不是出口,如果是出口 if (IsExit(cur, entry)) { //a). 比較當前的cur_path和short_path的長短 SeqStackDebugPrintPoint(cur_path, "找到一條路徑!"); if (SeqStackSize(cur_path) < SeqStackSize(short_path) || SeqStackSize(short_path) == 0) { //b). 如果cur_path比short_path小, // 或者short_path為空 // 用cur_path替換short_path。 SeqStackAssign(cur_path, short_path); printf("當前路徑是一條比較短的路徑\n"); } //c). 讓cur_path出棧,同時返回到上一層的棧幀 SeqStackPop(cur_path); return; } //4. 按照一定的順序遞迴的呼叫該函式完成鄰接點的判定 Point up = cur; up.row -= 1; _GetShortPath(maze, up, entry, cur_path, short_path); Point right = cur; right.col += 1; _GetShortPath(maze, right, entry, cur_path, short_path); Point down = cur; down.row += 1; _GetShortPath(maze, down, entry, cur_path, short_path); Point left = cur; left.col -= 1; _GetShortPath(maze, left, entry, cur_path, short_path); //5. 回溯到上一個位置,先將cur_path出棧,再return SeqStackPop(cur_path); return; } void GetShortPath(Maze* maze, Point entry) { //遞迴 SeqStack short_path; //相當於擂臺 SeqStack cur_path; //當前找到的路徑 SeqStackInit(&short_path); SeqStackInit(&cur_path); _GetShortPath(maze, entry, entry, &cur_path, &short_path); SeqStackDebugPrintPoint(&short_path, "最短路徑是"); } int CanStayWithCycle(Maze* maze, Point cur, Point pre) { //1. 判定當前點是否在地圖上,如果不在就不能落腳 if (cur.row < 0 || cur.row >= MAZE_ROW || cur.col < 0 || cur.col >= MAZE_COL) { return 0; } //2. 判定當前點的值是否為1 int cur_value = maze->map[cur.row][cur.col]; if (cur_value == 1) { return 1; } //3. 判定當前點的值和前一個點的值得大小是否滿足 // cur_value - 1 > pre_value 時才能落腳 if (pre.row >= 0 && pre.row < MAZE_ROW && pre.col >= 0 && pre.col < MAZE_COL) { int pre_value = maze->map[pre.row][pre.col]; if (cur_value - 1 > pre_value) { return 1; } } return 0; } void MarkWithCycle(Maze* maze, Point cur, Point pre) { if (pre.row < 0 || pre.row >= MAZE_ROW || pre.col < 0 || pre.col >= MAZE_COL) { //pre非法 maze->map[cur.row][cur.col] = 2; return; } int pre_value = maze->map[pre.row][pre.col]; maze->map[cur.row][cur.col] = pre_value + 1; } void _GetShortPathWithCycle(Maze* maze, Point cur, Point pre, Point entry, SeqStack* cur_path, SeqStack* short_path) { //1. 判定當前點是否能落腳(判定落腳方式有變化) if (!CanStayWithCycle(maze, cur, pre)) { return; } //2. 如果能落腳,就標記當前點(標記的方式也有變化),把當前點入棧 MarkWithCycle(maze, cur, pre); SeqStackPush(cur_path, cur); //3. 判定當前點是否是出口 if (IsExit(cur, entry)) { // a). 判定cur_path 和 short_path 的大小 SeqStackDebugPrintPoint(cur_path, "找到一條路徑!"); if (SeqStackSize(cur_path) < SeqStackSize(short_path) || SeqStackSize(short_path) == 0) { // b). 如果cur_path比較小,就替換short_path SeqStackAssign(cur_path, short_path); } // c). 出棧,並回溯 SeqStackPop(cur_path); return; } //4. 如果不是出口,以當前點為基準,探測四個方向,遞迴的呼叫該函式 Point up = cur; up.row -= 1; _GetShortPathWithCycle(maze, up, cur, entry, cur_path, short_path); Point right = cur; right.col += 1; _GetShortPathWithCycle(maze, right, cur, entry, cur_path, short_path); Point down = cur; down.row += 1; _GetShortPathWithCycle(maze, down, cur, entry, cur_path, short_path); Point left = cur; left.col -= 1; _GetShortPathWithCycle(maze, left, cur, entry, cur_path, short_path); //5. 四個方向都探測完了,出棧,並回溯 SeqStackPop(cur_path); return; } void GetShortPathWithCycle(Maze* maze, Point entry) { SeqStack short_path; //相當於擂臺 SeqStack cur_path; //當前找到的路徑 SeqStackInit(&short_path); SeqStackInit(&cur_path); Point pre = { -1, -1 }; _GetShortPathWithCycle(maze, entry, pre, entry, &cur_path, &short_path); SeqStackDebugPrintPoint(&short_path, "最短路徑是"); } ////////////////////////////////////////// //////////以下是測試程式碼////////////////// ////////////////////////////////////////// #define TEST_HEADER printf("\n======%s======\n", __FUNCTION__) void Test1() { TEST_HEADER; Maze maze; MazeInit(&maze); MazePrint(&maze); } void Test2() { TEST_HEADER; Maze maze; MazeInit(&maze); Point entry = { 0, 1 }; HasPath(&maze, entry); MazePrint(&maze); } void Test3() { TEST_HEADER; Maze maze; MazeInit(&maze); Point entry = { 0, 1 }; HasPathByLoop(&maze, entry); //非遞迴 MazePrint(&maze); } void Test4() { TEST_HEADER; Maze maze; MazeInitMultiExit(&maze); Point entry = { 0, 1 }; GetShortPath(&maze, entry); MazePrint(&maze); } void Test5() { TEST_HEADER; Maze maze; MazeInitMultiExitWithCycle(&maze); Point entry = { 0, 1 }; GetShortPathWithCycle(&maze, entry); MazePrint(&maze); } int main() { Test1(); Test2(); Test3(); Test4(); Test5(); system("pause"); return 0; }