问题描述:
迷宫是一个矩形区域,它仅有一个入口和一个出口。在迷宫的内部不能穿越的墙或障碍,也包含一些可以行走的通路。现在从入口出发,想要到达出口。用回溯思想设计算法,找到出口。
基本要求:
- 使用堆栈实现迷宫中寻找路径算法;
- 只有4个方向,分别为东、南、西、北;
- 不能走到迷宫外,‘#’代表障碍物,无法通行;
- 提供测试代码,给出测试结果;
算法思想:
回溯算法实际上是一个类似枚举的探索尝试过程,主要是在探索尝试过程中寻找问题的解,当发现当前不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标。就退回一步重新选择,这种走不通就退回再走的方法为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法的美称”。
在这里运用回溯思想(堆栈实现),在迷宫中找路,解决迷宫问题。本算法主要包括:回溯寻找路径部分。
测试迷宫:
char maze[MaxSize][MaxSize]=
{{'G', '.', '#', '#', '#', '#', '#', '#', '#', '#','.','.','.','#','#','#'},
{'#', '.', '#', '#', '#', '.', '#', '.', '.', '#','.','.','.','.','.','#'},
{'#', '.', '#', '#', '#', '.', '#', '.', '#', '#','.','#','.','.','.','#'},
{'#', '.', '#', '#', '#', '.', '.', '.', '.', '#','.','#','#','.','#','#'},
{'#', '.', '.', '.', '.', '.', '.', '.', '.', '.','.','.','#','.','#','#'},
{'#', '.', '.', '.', '.', '.', '.', '.', '.', '.','.','.','#','.','#','#'},
{'#', '.', '.', '.', '#', '#', '#', '#', '#', '.','.','.','#','.','.','#'},
{'#', '.', '.', '.', '.', '.', '.', '#', '.', '.','.','.','#','.','.','#'},
{'#', '.', '.', '.', '.', '.', '.', '#', '.', '.','.','.','.','#','.','#'},
{'#', '.', '#', '#', '#', '#', '#', '#', '.', '.','.','.','.','#','.','#'},
{'#', '.', '#', '.', '.', '.', '.', '.', '.', '.','.','.','.','#','.','#'},
{'#', '.', '#', '.', '.', '.', '.', '.', '#', '.','.','.','.','#','.','#'},
{'#', '.', '#', '.', '#', '#', '#', '.', '#', '.','#','#','.','.','.','#'},
{'#', '.', '.', '.', '#', '#', '#', '.', '#', '.','#','#','.','S','.','.'},
{'#', '.', '.', '.', '#', '#', '#', '.', '#', '.','#','#','.','.','.','.'},
{'#', '.', '#', '#', '#', '#', '.', '.', '#', '.','.','#','#','#','#','.'},};
注解:
‘ ’where the robot can move (open positions)
'#' -obstacles (blocked positions)
'S' -start position
'G' -goal
回溯路径寻找:
从起点开始进行路径寻找。在添加边界后每个位置有四种可能的移动方向:右、下、左和上。判断当前位置的右、下、左和上位置能否通行,即查看迷宫对应位置是否为0。若为0则可通行,将当前位置移动到对应方向,原位置入堆栈。若为1则不可通行,尝试下一个方向。若四个方向均不可通行,则当前栈顶元素出栈,尝试下一个方向。即递归回溯尝试所有的可能位置,直到当前位置等于出口位置。将已通过的位置置1,以免重复走。将走过的路径记录下来,即是寻找的可行路径。本算法将“在搜索过程中某一个时刻所在迷宫地图中的位置“记为“当前位置”,那么求解迷宫路径算法的基本思想是:若“当前位置”可通行,那么将“当前位置”纳入求解路径中(压到栈中),并朝“下一个位置”继续探索,即把“下一位置”切换到“当前位置”,如此重复下去,直到找到出口;如果当前位置不可以通行,则顺着来的方向退回到“前一个通道位置”,然后再朝着其他方向探索下去;如果该通道块的4个方向(东西南北)都不可通,那么就在该路径(栈)中删除该通道位置(删除栈顶元素),再继续退回到“前一个通道位置”继续探索,即再获取栈顶元素作为“当前位置继续上述的重复探索。
总体流程图如下:
这里我们首先定义基本的方向:
这是定义x,y的方向
这里我们需要写出堆栈的基本数据结构和基本操作:
堆栈里面存取的是当前点的x和y坐标:
typedef struct snode
{
int x; //定义该点的x坐标
int y; //定义该点的y坐标
struct snode *next;
}LSNode;
堆栈基本的操作函数
堆栈的程序如下:
#include "stack.h"
//链表初始化函数
void StackInitiate(LSNode **head)
{
*head = (LSNode *)malloc(sizeof(LSNode));
(*head)->next = NULL;
}
//判断堆栈是否为空函数
int StackNotEmpty(LSNode *head)
{
if(head->next == NULL) return 0;
else return 1;
}
//入栈操作函数
void StackPush(LSNode *head,int x,int y)
{
LSNode *p;
p=(LSNode *)malloc(sizeof(LSNode));
p->x = x;
p->y = y;
p->next = head->next;
head->next = p;
}
//出栈操作函数
int StackPop(LSNode *head,int *x,int *y)
{
LSNode *p = head->next;
if(p == NULL)
{
printf("堆栈已空,无元素可出!\n");
return 0;
}
head->next = p->next;
*x = p->x;
*y = p->y;
free(p);
return 1;
}
//取栈顶元素操作函数
int StackTop(LSNode *head,int *x,int *y)
{
LSNode *p = head->next;
if(p == NULL)
{
printf("堆栈已空出错!\n");
return 0;
}
*x = p->x;
*y = p->y;
return 1;
}
//销毁堆栈
void StackDestroy(LSNode *head)
{
LSNode *p,*p1;
p = head;
while(p!=NULL)
{
p1 = p;
p = p->next;
free(p1);
}
}
堆栈的头文件如下:
#ifndef _STACK_H
#define _STACK_H
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <math.h>
typedef struct snode
{
int x; //定义该点的x坐标
int y; //定义该点的y坐标
struct snode *next;
}LSNode;
void StackInitiate(LSNode **head);
int StackNotEmpty(LSNode *head);
void StackPush(LSNode *head,int x,int y);
int StackPop(LSNode *head,int *x,int *y);
int StackTop(LSNode *head,int *x,int *y);
void StackDestroy(LSNode *head);
#endif // _STACK_H
接下里我们需要定义迷宫位置的结构体及相应的操作函数:
typedef struct{
int x; //定义位置的坐标x
int y; //定义位置的坐标y
int east; //定义东方向的标志位
int south; //定义南方向的标志位
int west; //定义西方向的标志位
int north; //定义北方向的标志位
int flag; //定义当前位置是否方位的标志位
}Location;
位置初始化操作函数:
//位置初始化函数.
void LocationInitiate(Location array[],int n)
{
int i;
for(i=0;i<n;i++)
{
array[i].x = -1;
array[i].y = -1;
array[i].east = 0;
array[i].south = 0;
array[i].west = 0;
array[i].north = 0;
array[i].flag = 0;
}
}
判断方位是否操作函数:
//判断当前位置是否访问过
int WhetherVisited(int x,int y,Location array[],int n)
{
int i;
for(i=0;i<n;i++)
{
if(array[i].x==x && array[i].y==y)
{
return 1;
}
}
return 0;
}
//东方向是否访问
int WhetherVisitedEast(int x,int y,Location array[],int n)
{
int i;
for(i=0;i<n;i++)
{
if(array[i].x==x && array[i].y==y && array[i].east)
{
return 1;
}
}
return 0;
}
//南方向是否访问
int WhetherVisitedSouth(int x,int y,Location array[],int n)
{
int i;
for(i=0;i<n;i++)
{
if(array[i].x==x && array[i].y==y && array[i].south)
{
return 1;
}
}
return 0;
}
//西方向是否访问
int WhetherVisitedWest(int x,int y,Location array[],int n)
{
int i;
for(i=0;i<n;i++)
{
if(array[i].x==x && array[i].y==y && array[i].west)
{
return 1;
}
}
return 0;
}
//北方向是否访问
int WhetherVisitedNorth(int x,int y,Location array[],int n)
{
int i;
for(i=0;i<n;i++)
{
if(array[i].x==x && array[i].y==y && array[i].north)
{
return 1;
}
}
return 0;
}
找到迷宫起点终点操作函数:
//找到迷宫起点操作函数
int FindMazeStart(char maze[][MaxSize],int x_size,int y_size,int *start_x,int *start_y)
{
int i,j;
for(i=0;i<x_size;i++)
{
for(j=0;j<y_size;j++)
{
if(maze[i][j] =='S')
{
*start_x = i;
*start_y = j;
return 1;
}
}
}
printf("没有找到起点!\n");
return 0;
}
//找到迷宫终点操作函数
int FindMazeEnd(char maze[][MaxSize],int x_size,int y_size,int *end_x,int *end_y)
{
int i,j;
for(i=0;i<x_size;i++)
{
for(j=0;j<y_size;j++)
{
if(maze[i][j] =='G')
{
*end_x = i;
*end_y = j;
return 1;
}
}
}
printf("没有找到终点!\n");
return 0;
}
得到优先级操作函数:
这里我们一共有4种位置
这种情况下我们需要向东走或者向南走才能快速的到达终点,判断先是向东走还是向南走,这里我们分别求|end_x-cur_x|与|end_y-cur_y|,然后比较两个的绝对值大小,就能够赋予相应的优先级。
这种情况下我们需要向西走或者向南走才能快速的到达终点,判断先是向东走还是向南走,这里我们分别求|end_x-cur_x|与|end_y-cur_y|,然后比较两个的绝对值大小,就能够赋予相应的优先级。
这种情况下我们需要向北走或者向东走才能快速的到达终点,判断先是向东走还是向南走,这里我们分别求|end_x-cur_x|与|end_y-cur_y|,然后比较两个的绝对值大小,就能够赋予相应的优先级。
这种情况下我们需要向北走或者向西走才能快速的到达终点,判断先是向东走还是向南走,这里我们分别求|end_x-cur_x|与|end_y-cur_y|,然后比较两个的绝对值大小,就能够赋予相应的优先级。
/*
优先级方向函数
东->4
南->3
西->2
北->1
*/
void DirectionPriority(int Priorityflag[],int x,int y,int end_x,int end_y)
{
int temp_x,temp_y;
temp_x = end_x - x;
temp_y = end_y - y;
if(temp_x>=0 && temp_y>=0)
{
if(temp_x>=temp_y)
{
Priorityflag[0] = 3;
Priorityflag[1] = 4;
Priorityflag[2] = 2;
Priorityflag[3] = 1;
}
else
{
Priorityflag[0] = 4;
Priorityflag[1] = 3;
Priorityflag[2] = 2;
Priorityflag[3] = 1;
}
}
else if(temp_x<0 && temp_y>=0)
{
temp_x = abs(temp_x);
if(temp_x>=temp_y)
{
Priorityflag[0] = 1;
Priorityflag[1] = 4;
Priorityflag[2] = 3;
Priorityflag[3] = 2;
}
else
{
Priorityflag[0] = 4;
Priorityflag[1] = 1;
Priorityflag[2] = 3;
Priorityflag[3] = 2;
}
}
else if(temp_x>=0 && temp_y<0)
{
temp_y = abs(temp_y);
if(temp_x>=temp_y)
{
Priorityflag[0] = 3;
Priorityflag[1] = 2;
Priorityflag[2] = 4;
Priorityflag[3] = 1;
}
else
{
Priorityflag[0] = 2;
Priorityflag[1] = 3;
Priorityflag[2] = 4;
Priorityflag[3] = 1;
}
}
else
{
temp_x = abs(temp_x);
temp_y = abs(temp_y);
if(temp_x>=temp_y)
{
Priorityflag[0] = 1;
Priorityflag[1] = 2;
Priorityflag[2] = 3;
Priorityflag[3] = 4;
}
else
{
Priorityflag[0] = 2;
Priorityflag[1] = 1;
Priorityflag[2] = 3;
Priorityflag[3] = 4;
}
}
}
打印路径函数:
//打印路线函数
void PrintMazePath(LSNode *head,int start_x,int start_y,int end_x,int end_y,char maze[][MaxSize],int TransFlag)
{
LSNode *cur_Node;
int i,j;
int cur_x,cur_y;
char tempmaze[MaxSize][MaxSize];
cur_Node = head->next;
if(cur_Node == NULL)
{
printf("该堆栈为空,打印路径失败!\n");
exit(0);
}
while(StackNotEmpty(head))
{
StackPop(head,&cur_x,&cur_y);
maze[cur_x][cur_y]='X';
}
if(!TransFlag)
{
maze[start_x][start_y] = 'S';
maze[end_x][end_y] = 'G';
for(i=0;i<MaxSize;i++)
{
for(j=0;j<MaxSize;j++)
{
printf("%c ",maze[i][j]);
}
printf("\n");
}
}
if(TransFlag)
{
for(i=0;i<MaxSize;i++)
{
for(j=0;j<MaxSize;j++)
{
tempmaze[i][j] = maze[j][i];
}
}
tempmaze[start_y][start_x] = 'S';
tempmaze[end_y][end_x] = 'G';
for(i=0;i<MaxSize;i++)
{
for(j=0;j<MaxSize;j++)
{
printf("%c ",tempmaze[i][j]);
}
printf("\n");
}
}
}
迷宫路径寻找函数:
//迷宫路径寻找函数
void FindMazePath(int start_x,int start_y,int end_x,int end_y,char maze[][MaxSize],int TransFlag)
{
LSNode *Stack;
int cur_x,cur_y,cur_east,cur_south,cur_west,cur_north;
int pre_x,pre_y;
int LocationCount = 0;
int LocationFind = 0;
int PriorityCount = 0;
int Priorityflag[4]={0,0,0,0}; //定义优先级数组
Location array[StoreSize]; //定义坐标元素数组
LocationInitiate(array,StoreSize);//初始化位置结构体
StackInitiate(&Stack); //创建一个堆栈
StackPush(Stack,start_x,start_y);//将起点的x坐标,y坐标入堆栈
while(StackNotEmpty(Stack))
{
StackPop(Stack,&cur_x,&cur_y);
if(Priorityflag[PriorityCount]==0)
{
PriorityCount = 0;
DirectionPriority(Priorityflag,cur_x,cur_y,end_x,end_y);
}
cur_east = WhetherVisitedEast(cur_x,cur_y,array,StoreSize);
cur_south = WhetherVisitedSouth(cur_x,cur_y,array,StoreSize);
cur_west = WhetherVisitedWest(cur_x,cur_y,array,StoreSize);
cur_north = WhetherVisitedNorth(cur_x,cur_y,array,StoreSize);
if(cur_x == end_x&&cur_y == end_y)
{
printf("Finding path successfully!\n");
StackPush(Stack,cur_x,cur_y);
break;
}
else if((!cur_east)&&(cur_y+1<MaxSize) && maze[cur_x][cur_y+1]!='#'&& Priorityflag[PriorityCount]==4)
{
cur_east = 1;//将东方向的标志位置一;
pre_x = cur_x;
pre_y = cur_y+1;
if(!WhetherVisited(pre_x,pre_y,array,StoreSize))
{
array[LocationCount].x = pre_x;
array[LocationCount].y = pre_y;
array[LocationCount].flag = 1;
array[LocationCount].west=1;
LocationCount++;
}
for(LocationFind = 0;LocationFind<StoreSize;LocationFind++)
{
if(array[LocationFind].x == cur_x && array[LocationFind].y == cur_y)
array[LocationFind].east = 1;
}
StackPush(Stack,cur_x,cur_y);
StackPush(Stack,pre_x,pre_y);
Priorityflag[PriorityCount]=0;
continue;
}
else if(!cur_south && (cur_x+1<MaxSize) && maze[cur_x+1][cur_y]!='#' && Priorityflag[PriorityCount]==3)
{
cur_south = 1;//将南方向的标志位置一;
pre_x = cur_x+1;
pre_y = cur_y;
if(!WhetherVisited(pre_x,pre_y,array,StoreSize))
{
array[LocationCount].x = pre_x;
array[LocationCount].y = pre_y;
array[LocationCount].flag = 1;
array[LocationCount].north=1;
LocationCount++;
}
for(LocationFind = 0;LocationFind<StoreSize;LocationFind++)
{
if(array[LocationFind].x == cur_x && array[LocationFind].y == cur_y)
array[LocationFind].south = 1;
}
StackPush(Stack,cur_x,cur_y);
StackPush(Stack,pre_x,pre_y);
Priorityflag[PriorityCount]=0;
continue;
}
else if(!cur_west&&(cur_y-1>=0) && maze[cur_x][cur_y-1]!='#'&&Priorityflag[PriorityCount]==2)
{
cur_west = 1;//将西方向的标志位置一;
pre_x = cur_x;
pre_y = cur_y-1;
if(!WhetherVisited(pre_x,pre_y,array,StoreSize))
{
array[LocationCount].x = pre_x;
array[LocationCount].y = pre_y;
array[LocationCount].flag = 1;
array[LocationCount].east=1;
LocationCount++;
}
for(LocationFind = 0;LocationFind<StoreSize;LocationFind++)
{
if(array[LocationFind].x == cur_x && array[LocationFind].y == cur_y)
array[LocationFind].west = 1;
}
StackPush(Stack,cur_x,cur_y);
StackPush(Stack,pre_x,pre_y);
Priorityflag[PriorityCount]=0;
continue;
}
else if(!cur_north && (cur_x-1>=0) && maze[cur_x-1][cur_y]!='#'&&Priorityflag[PriorityCount]==1)
{
cur_north = 1;//将北方向的标志位置一;
pre_x = cur_x-1;
pre_y = cur_y;
if(!WhetherVisited(pre_x,pre_y,array,StoreSize))
{
array[LocationCount].x = pre_x;
array[LocationCount].y = pre_y;
array[LocationCount].flag = 1;
array[LocationCount].south=1;
LocationCount++;
}
for(LocationFind = 0;LocationFind<StoreSize;LocationFind++)
{
if(array[LocationFind].x == cur_x && array[LocationFind].y == cur_y)
array[LocationFind].north = 1;
}
StackPush(Stack,cur_x,cur_y);
StackPush(Stack,pre_x,pre_y);
Priorityflag[PriorityCount]=0;
}
else
{
if(Priorityflag[PriorityCount]!=0&&PriorityCount<3)
{
PriorityCount++;
StackPush(Stack,cur_x,cur_y);
}
else
{
PriorityCount = 0;
}
}
}
if(cur_x!=end_x||cur_y!=end_y) //判断是否有路径从起点到终点
{
printf("There are not paths to the end!\n");
exit(0);
}
PrintMazePath(Stack,start_x,start_y,end_x,end_y,maze,TransFlag);
StackDestroy(Stack);
}
迷宫头文件定义如下:
#ifndef _MAZE_H
#define _MAZE_H
#include "stack.h"
#define MaxSize 16
#define StoreSize 256
typedef struct{
int x;
int y;
int east;
int south;
int west;
int north;
int flag;
}Location;
/*内部调用函数*/
void LocationInitiate(Location array[],int n);
int WhetherVisited(int x,int y,Location array[],int n);
void PrintMazePath(LSNode *head,int start_x,int start_y,int end_x,int end_y,char maze[][MaxSize],int TransFlag);
int WhetherVisitedEast(int x,int y,Location array[],int n);
int WhetherVisitedSouth(int x,int y,Location array[],int n);
int WhetherVisitedWest(int x,int y,Location array[],int n);
int WhetherVisitedNorth(int x,int y,Location array[],int n);
void DirectionPriority(int Priorityflag[],int x,int y,int end_x,int end_y);
/*外部调用函数*/
void FindMazePath(int start_x,int start_y,int end_x,int end_y,char maze[][MaxSize],int TransFlag);
int FindMazeEnd(char maze[][MaxSize],int x_size,int y_size,int *end_x,int *end_y);
int FindMazeStart(char maze[][MaxSize],int x_size,int y_size,int *start_x,int *start_y);
#endif // _MAZE_H
主函数如下:
#include "stack.h"
#include "maze.h"
extern char maze[MaxSize][MaxSize];
char Transmaze [MaxSize][MaxSize];
int main()
{
int start_x,start_y;
int end_x,end_y;
int i,j;
int TransFlag = 0;
if(FindMazeStart(maze,MaxSize,MaxSize,&start_x,&start_y)&&FindMazeEnd(maze,MaxSize,MaxSize,&end_x,&end_y))
{
if(start_y <= end_y || start_x <= end_x)
{
printf("Original maze:\n");
for(i=0;i<MaxSize;i++)
{
for(j=0;j<MaxSize;j++)
{
printf("%c ",maze[i][j]);
}
printf("\n");
}
printf("\n");
FindMazePath(start_x,start_y,end_x,end_y,maze,TransFlag);
}
else
{
TransFlag = 1;
printf("Original maze:\n");
for(i=0;i<MaxSize;i++)
{
for(j=0;j<MaxSize;j++)
{
printf("%c ",maze[i][j]);
}
printf("\n");
}
printf("\n");
printf("Transposition maze:\n");
for(i=0;i<MaxSize;i++)
{
for(j=0;j<MaxSize;j++)
{
Transmaze[j][i] = maze[i][j];
}
}
for(i=0;i<MaxSize;i++)
{
for(j=0;j<MaxSize;j++)
{
printf("%c ",Transmaze[i][j]);
}
printf("\n");
}
printf("\n");
FindMazeStart(Transmaze,MaxSize,MaxSize,&start_x,&start_y);
FindMazeEnd(Transmaze,MaxSize,MaxSize,&end_x,&end_y);
FindMazePath(start_x,start_y,end_x,end_y,Transmaze,TransFlag);
}
}
else
printf("没有起点或者没有终点,寻找路径失败\n");
return 0;
}
最终结果:
完。