问题引出
顾名思义,有多条出口的迷宫,迷宫内部各路径有交叉,成环,求其最短路径
例如:
解题思路
整体思路和多出口不带环迷宫最短路径求解一样,只是这次更改的是标记函数 Mark 与 判断是否能够落脚函数 Canstay
因为要判断路径是否带环,以前单一的将走过的路径标记为2已经不能满足我们的需求了
所以这次需要将走过的块标记为当前步数,即每块走过的位置都一个不同的标记,这样,我们就能识别出环路径了
对于多出口不带环迷宫最短路径求解不清楚的可以移步
方法实现
这里我们要用到栈的一些操作,对于栈的概念和操纵还不太理解的可以移步
maze.c
#include "seqstack.h"
#include <stdio.h>
// 迷宫的长宽
#define ROW 6 // 行
#define COL 6 // 列
typedef struct Maze {
int map[COL][ROW];
} Maze;
// 创建迷宫
void createMaze(Maze* maze) {
int tmp[COL][ROW] = {
{ 0, 1, 0, 0, 0, 0},
{ 1, 1, 1, 1, 1, 0},
{ 0, 1, 0, 0, 1, 0},
{ 0, 1, 0, 0, 1, 0},
{ 0, 1, 1, 1, 1, 1},
{ 0, 1, 0, 0, 0, 0},
};
int i = 0;
for( ; i < COL; i++) {
int j = 0;
for( ; j < ROW; j++) {
maze->map[i][j] = tmp[i][j];
}
}
}
// 打印迷宫
void printMaze(Maze maze) {
int i = 0;
// 装饰性打印
printf(" +");
for( ; i < ROW*2; i++) {
printf("-");
}
printf("-+");
printf("\n");
// 打印开始
for( i = 0; i < COL; i++) {
int j = 0;
printf(" | ");
for( ; j < ROW; j++) {
printf("%d ", maze.map[i][j]);
}
printf("|\n");
}
// 装饰性打印
printf(" +");
for( i = 0; i < ROW*2; i++) {
printf("-");
}
printf("-+\n");
}
// 判断当前点是否能够落脚
// 传入新参数 当前点前一个点 pre
// 比较 pos 与 pre 两个点的值,来判断当前是否能够落脚
int Canstay(Maze* maze, Coordinates pos, Coordinates pre) {
// 越界
if(pos.row>=0 && pos.row<ROW && pos.col>=0 && pos.col<COL) {
if(maze->map[pos.col][pos.row] == 0) {
return 0;
}
// 当前位置还没有被标记过,可以落脚
if(maze->map[pos.col][pos.row] == 1) {
return 1;
}
// 比较 pos 与 pre 两个点的值,来判断当前是否能够落脚
// pre = pos 不能落脚
// pre > pos 不能落脚
// pre = pos-1 不能落脚
// pre < pos 可以落脚
if(maze->map[pos.col][pos.row] > maze->map[pre.col][pre.row]+1) {
return 1;
} else {
return 0;
}
}
return 0;
}
// 将当前点进行标记
// 标记为前一个点的值+1
void Mark(Maze* maze, Coordinates pos, Coordinates pre) {
maze->map[pos.col][pos.row] = maze->map[pre.col][pre.row]+1;
}
// 是否为出口
int isExit(Maze* maze, Coordinates entry, Coordinates pos) {
if((pos.row==entry.row) && (pos.col==entry.col)) {
return 0;
}
if(pos.row==0 || pos.row==ROW-1 || pos.col==0 || pos.col==COL-1) {
return 1;
}
return 0;
}
// 打印路径
void printPath(SeqStack stack) {
Datatype value;
while(SeqStackTop(&stack, &value)) {
printf("(%d, %d)\n", value.row, value.col );
SeqStackPop(&stack);
}
}
// 比较两个栈长度,s1 <= s2,返回 <=0,否则, >0
int compareStack(SeqStack stack1, SeqStack stack2) {
Datatype value;
int ret = 0;
int count1 = 0;
int count2 = 0;
while(ret=SeqStackTop(&stack1, &value)) {
SeqStackPop(&stack1);
count1++;
}
while(ret=SeqStackTop(&stack2, &value)) {
SeqStackPop(&stack2);
count2++;
}
return count1-count2;
}
// 将 src 栈的所有元素拷贝到 dst 栈中
void cloneStack(SeqStack* dest, SeqStack* src) {
// 由于是顺序表构成的栈,所以首先释放 dest 空间
SeqStackDestroy(dest);
// 创建新空间
dest->size = src->size;
dest->capacity = src->capacity;
dest->data = malloc(sizeof(SeqStack) * src->capacity);
int i = 0;
for(i = 0; i < src->size; i++) {
dest->data[i] = src->data[i];
}
}
void getMazePath(Maze* maze, Coordinates entry) {
// 构建两个栈,一个栈为 cur_path,保存当前走过的路径,另一个栈为 short_path,保存最短的路径
SeqStack cur_path;
SeqStack short_path;
SeqStackInit(&cur_path);
SeqStackInit(&short_path);
// 判断当前该点是否能落脚,不能落脚直接返回
if(!Canstay(maze, entry, entry)) {
return;
}
// 标记当前点,并且入栈到cur_path
Mark(maze, entry, entry);
SeqStackPush(&cur_path, entry);
int ret = 1;
while(ret == 1) {
// 进入循环,先拿到当前的栈顶元素(该元素一定合法)
Coordinates cur;
ret = SeqStackTop(&cur_path, &cur);
// 判断当前点是否为出口,是出口就找到了一条路
if(isExit(maze, entry, cur) == 1) {
// 是出口,将 两个栈中的元素进行比较
// 如果short_path < cur_path,返回 <0,否则 >0
int compare = compareStack(cur_path, short_path);
// 第一次 short_path 为空
Datatype value;
if(SeqStackTop(&short_path, &value) == 0) {
cloneStack(&short_path, &cur_path);
printf("找到一条路径[%d, %d]\n", cur.col, cur.row);
}
// 如果cur_path保存的路径比short_path中保存的路径长,就将当前 cur_path 路径 保存至short_path中
if(compare < 0) {
cloneStack(&short_path, &cur_path);
printf("找到一条更短的路径[%d, %d]\n", cur.col, cur.row);
} else {
// 否则,继续寻找其他路径
printf("又找到一条路径[%d, %d]\n", cur.col, cur.row);
}
// 出栈当前栈顶元素,回溯
SeqStackPop(&cur_path);
}
// 如果不是,按顺时针方向依次取相邻点,并判定他们是否能落脚,如果可以,入栈,进入下一轮循环
// 上
Coordinates up;
up.col = cur.col-1;
up.row = cur.row;
if(Canstay(maze, up, cur)) {
Mark(maze, up, cur);
SeqStackPush(&cur_path, up);
continue;
}
// 右
Coordinates right;
right.col = cur.col;
right.row = cur.row+1;
if(Canstay(maze, right, cur)) {
Mark(maze, right, cur);
SeqStackPush(&cur_path, right);
continue;
}
// 下
Coordinates down;
down.col = cur.col+1;
down.row = cur.row;
if(Canstay(maze, down, cur)) {
Mark(maze, down, cur);
SeqStackPush(&cur_path, down);
continue;
}
// 左
Coordinates left;
left.col = cur.col;
left.row = cur.row-1;
if(Canstay(maze, left, cur)) {
Mark(maze, left, cur);
SeqStackPush(&cur_path, left);
continue;
}
// 四个点都不能落脚,出栈当前栈顶元素,回溯
SeqStackPop(&cur_path);
}
// 打印路径
printPath(short_path);
}
int main() {
Maze maze;
Coordinates entry;
entry.col = 0;
entry.row = 1;
// 创建迷宫
createMaze(&maze);
// 打印迷宫
printMaze(maze);
// 求解迷宫线路
getMazePath(&maze, entry);
printMaze(maze);
}