迷宮問題——回溯法
阿新 • • 發佈:2018-11-10
一、Maze.h
#ifndef __Maze_h__
#define __Maze_h__
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <Windows.h>
#define N 6
#define Initsize 5 //初始儲存空間
#define Increment 2 //每次增量
typedef struct Pos
{
int row;
int col;
}Pos;
typedef Pos DataType;
typedef struct Maze
{
int maze[N][N];
Pos entry;
}Maze;
typedef struct Stack
{
DataType* _array;
size_t _size; //有效資料個數
size_t _capacity; //容量
}Stack;
void StackInit(Stack *s);
void CheckCapacity(Stack* s);
void StackPush(Stack* s, DataType x);
void StackPop(Stack* s);
DataType StackTop(Stack* s);
size_t StackSize(Stack* s);
void StackDestroy(Stack* s);
void MazeInit(Maze* m);
void MazePrint(Maze* m);
int MazeCheckIsAccess(Pos pos);
void MazeGetPath(Maze* m, Pos entry, Stack *path);
void MazeGetShortPath(Maze* m, Pos entry, Stack *path, Stack *shortpath, int flag);
#endif __Maze_h__
二、Maze.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Maze.h"
//棧初始化
void StackInit(Stack* s)
{
assert(s);
s->_size = 0;
s->_capacity = Initsize;
s->_array = (DataType*)malloc(Initsize*sizeof(DataType));
assert(s->_array);
memset(s->_array, 0, s->_capacity * sizeof(DataType));
}
//擴容
void CheckCapacity(Stack* s)
{
assert(s);
if (s->_size >= s->_capacity)
{
DataType* ptr = (DataType*)realloc(s->_array, (s->_capacity + Increment)*sizeof(DataType));
assert(ptr);
s->_array = ptr;
s->_capacity += Increment;
}
}
//入棧
void StackPush(Stack* s, DataType x)
{
assert(s);
CheckCapacity(s);
s->_array[s->_size] = x;
s->_size++;
}
//出棧
void StackPop(Stack* s)
{
assert(s);
if (s->_size == 0)
{
return;
}
else
{
s->_size--;
}
}
//獲取棧頂元素
DataType StackTop(Stack* s)
{
assert(s&&s->_size > 0);
return s->_array[(s->_size) - 1];
}
//棧長度
size_t StackSize(Stack* s)
{
assert(s);
return s->_size;
}
//銷燬
void StackDestroy(Stack* s)
{
free(s->_array);
s->_size = 0;
s->_capacity = 0;
}
//初始化迷宮
void MazeInit(Maze* m)
{
assert(m);
int a[N][N] =
{
{ 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 0 },
{ 0, 0, 1, 0, 1, 1 },
{ 0, 0, 1, 1, 1, 0 },
{ 0, 0, 1, 0, 0, 0 },
};
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
m->maze[i][j] = a[i][j];
}
}
m->entry.row = 5;
m->entry.col = 2;
}
//列印迷宮
void MazePrint(Maze* m)
{
assert(m);
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
printf("%d ", m->maze[i][j]);
}
printf("\n");
}
printf("\n");
}
//判斷是否為出口,假設出口在最後一列
int MazeCheckIsAccess(Pos pos)
{
if (pos.col == N - 1)
{
return 1;
}
return 0;
}
//單通路標記
void MarkPath(Maze** pm, Pos entry)
{
(*pm)->maze[entry.row][entry.col] = 2;
}
//尋找單通路——只能處理單條路徑
void MazeGetPath(Maze* m, Pos entry, Stack *path)
{
assert(m);
if (entry.row < 0 || entry.row >= N || entry.col < 0 || entry.col >= N) //座標不合法,直接返回
{
return;
}
if (m->maze[entry.row][entry.col] == 1) //如果是路,就入棧,並進行標記
{
StackPush(path, entry);
MarkPath(&m, entry);
}
else //否則,直接返回
{
return;
}
if (MazeCheckIsAccess(entry)) //判斷是否到達出口
{
printf("找到出口!\n");
printf("出口座標:%d %d\n", StackTop(path).row, StackTop(path).col);
printf("路徑長度為:%d\n", StackSize(path));
printf("路徑為:");
for (int i = 0; i < (int)path->_size; i++)
{
printf("(%d,%d) ", path->_array[i].row, path->_array[i].col);
}
printf("\n");
StackPop(path); //通路上其他非出口位置是經4個方向都找過後才Pop並返回上一位置;而出口位置是不需要查詢4個方向,直接Pop並返回上一位置
MazePrint(m);
return;
}
//如果沒有找到出口,分別在當前位置的4個方向繼續尋找
//上
Pos up_pos = entry;
up_pos.row--;
MazeGetPath(m, up_pos, path);
//下
Pos down_pos = entry;
down_pos.row++;
MazeGetPath(m, down_pos, path);
//左
Pos left_pos = entry;
left_pos.col--;
MazeGetPath(m, left_pos, path);
//右
Pos right_pos = entry;
right_pos.col++;
MazeGetPath(m, right_pos, path);
StackPop(path);
return;
}
//複製棧
void StackCopy(Stack *src, Stack *dst)
{
assert(src&&dst);
if (src->_size == 0)
{
dst->_size = 0;
}
else
{
if (dst->_capacity < src->_size)
{
DataType *ptr = (DataType *)realloc(dst->_array, src->_size*sizeof(DataType));
assert(ptr);
dst->_array = ptr;
dst->_capacity = src->_size;
}
dst->_size = src->_size;
memcpy(dst->_array, src->_array, sizeof(DataType)*src->_size);
}
}
//標記通路
void MarkPath1(Maze** pm, Pos entry, int flag)
{
(*pm)->maze[entry.row][entry.col] = flag;
}
//尋找最短路徑——可處理多條路徑
void MazeGetShortPath(Maze* m, Pos entry, Stack *path, Stack *shortpath, int flag)
{
assert(m);
if (entry.row < 0 || entry.row >= N || entry.col < 0 || entry.col >= N) //座標不合法,直接返回
{
return;
}
if (m->maze[entry.row][entry.col] == 1 || m->maze[entry.row][entry.col]>flag) //如果是路或者有更短的路,就入棧,並進行標記
{
StackPush(path, entry);
MarkPath1(&m, entry, flag++);
}
else //否則,直接返回
{
return;
}
if (MazeCheckIsAccess(entry)) //判斷是否到達出口
{
printf("找到出口!\n");
printf("出口座標:%d %d\n", StackTop(path).row, StackTop(path).col);
printf("路徑長度為:%d\n", StackSize(path));
printf("路徑為:");
for (int i = 0; i < (int)path->_size; i++)
{
printf("(%d,%d) ", path->_array[i].row, path->_array[i].col);
}
printf("\n");
if (StackSize(shortpath) == 0 || StackSize(path) < StackSize(shortpath))
{
StackCopy(path, shortpath); //儲存最短路徑
}
StackPop(path); //通路上其他非出口位置是經4個方向都找過後才Pop並返回上一位置;而出口位置是不需要查詢4個方向,直接Pop並返回上一位置
MazePrint(m);
return;
}
//如果沒有找到出口,分別在當前位置的4個方向繼續尋找
//上
Pos up_pos = entry;
up_pos.row--;
MazeGetShortPath(m, up_pos, path, shortpath, flag);
//下
Pos down_pos = entry;
down_pos.row++;
MazeGetShortPath(m, down_pos, path, shortpath, flag);
//左
Pos left_pos = entry;
left_pos.col--;
MazeGetShortPath(m, left_pos, path, shortpath, flag);
//右
Pos right_pos = entry;
right_pos.col++;
MazeGetShortPath(m, right_pos, path, shortpath, flag);
StackPop(path);
return;
}
三、Test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Maze.h"
int main()
{
Maze m;
MazeInit(&m);
MazePrint(&m);
Stack path;
Stack shortpath;
StackInit(&path);
StackInit(&shortpath);
MazeGetShortPath(&m, m.entry, &path, &shortpath, 1);
printf("最短路徑長度為:%d\n", StackSize(&shortpath));
printf("路徑為:");
for (int i = 0; i < (int)shortpath._size; i++)
{
printf("(%d,%d) ", shortpath._array[i].row, shortpath._array[i].col);
}
printf("\n");
StackDestroy(&path);
StackDestroy(&shortpath);
system("pause");
return 0;
}