資料結構之迷宮求解
阿新 • • 發佈:2019-02-08
//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;
}