您的位置:首页 > 其它

迷宫求解算法(栈DFS以及队列BFS)

2015-11-03 17:35 417 查看
我们首先给出一个迷宫,它的规格是5 * 5,在这里我使用int的二维数组表示迷宫,其中1表示障碍,0表示可以通行的道路,要求从(0,0)坐标走到(4, 4)坐标,并输出走过的坐标路径。

int maze[5][5] = {

0, 1, 0, 0, 0,

0, 1, 0, 1, 0,

0, 0, 0, 0, 0,

0, 1, 1, 1, 0,

0, 0, 0, 1, 0,

};


①使用栈来实现DFS(深度优先搜索)

首先,我们给出栈在迷宫中的的结构定义。

/*
*  x是迷宫中的横坐标
*  y是迷宫中的纵坐标
*  direction表示在当前坐标内要走的下一个方向
*  next指针则指向栈中的下一个坐标
*/
typedef struct LNode {
int x;
int y;
int direction;
struct LNode *next;
}LNode;

/*
* top指针指向栈的顶部
* 不需要base指针
*/
typedef struct {
LNode *top;
}Stack;


接着,我们定义一些栈的基本函数操作:

/*
* IntiStack()
* 初始化一个栈,传入一个Stack结构体的地址或结构指针
* top指针指向一个头结点,该头结点不包含坐标但next指针指向栈的顶部坐标结点
*/
void IntiStack(Stack *result){
result->top = (LNode *)malloc(sizeof(LNode));
result->top->next = NULL;
}


/*
* Push()
* 将一个新的坐标结点放入栈中,所使用的方法类似于表头插入法
* 其中direction是现在的坐标点下一步要走的方向
*/
void Push(Stack *result, int x, int y, int direction){
LNode *topNode = (LNode *)malloc(sizeof(LNode));
topNode->x = x;
topNode->y = y;
topNode->direction = direction;
topNode->next = result->top->next;
result->top->next = topNode;
}


/*
* Pop()
* 该函数用于走到迷宫的死路时向后退以及遍历输出结点
* 弹出top指针中next指针所指向的结点,也就是栈的顶部节点
* 类似于从表头删除一个节点,并返回该节点
*/
LNode *Pop(Stack *result){
LNode *temp = result->top->next;
if(temp != NULL){
result->top->next = temp->next;
}
temp->next = NULL;
return temp;
}


我们再给出main函数的定义

int main(void){
/*
* MAX的值为5
* 定义一个二维数组
* 输入迷宫的数值,并初始化一个栈
*/
int maze[MAX][MAX];
int i, j;
for(i = 0;i < MAX;i++){
for(j = 0;j < MAX;j++){
scanf("%d", &maze[i][j]);
}
}
Stack result;
IntiStack(&result);

/*
* 当处于一个迷宫结点的时候,我们应该分别检测四个方向是否有路走
* 暂定direction = 1, 2, 3, 4分别表示东,南,西,北
* flagDirection 则表示已经找到一个可行的方向,它是一个哨兵
* row, col表示当前结点的坐标
* 首先将(0, 0)结点入栈,direction默认从1开始,它还将经过一次顺时针遍        历,所以它的东方向即使是障碍也没关系
* 因为已经遍历过(0, 0)这个点我们不会再走这个点所以把它堵上
* current指针指向栈的顶部节点
*/
int flagDirect = 1;
int row = 0;
int col = 0;
int direction = 1;
Push(&result, row, col, direction);
maze[row][col] = 1;
LNode *current;

/*
* 设置了一个无限循环,跳出循环的条件是到达点(4, 4)
* 如何顺时针的遍历四个方向?通过使用switch判断可行的方向,当方向不可行的        时候direction加1测试下一个方向是否可行
* 但是当所有方向都不可行时证明该坐标是迷宫中的一条死路需要退回去
* 这时通过Pop操作出栈退回到有路可走的坐标点
* 之前把走过的坐标点堵上就是为了不再走重复的路避免在死路里循环,也方便出栈
*/
while(1){
if(row == (MAX - 1) && col == (MAX - 1)){
break;
}
flagDirect = 1;
current = result.top->next;
row = current->x;
col = current->y;
while(flagDirect){
switch(current->direction){
case 1:if(col + 1 < MAX && maze[row][col + 1] == 0){
maze[row][col] = 1;
col++;
Push(&result, row, col, 1);
flagDirect = 0;
}
else{
current->direction++;
}
break;
case 2:if(row + 1 < MAX && maze[row + 1][col] == 0){
maze[row][col] = 1;
row++;
Push(&result, row, col, 1);
flagDirect = 0;
}
else{
current->direction++;
}
break;
case 3:if(col - 1 >= 0 && maze[row][col - 1] == 0){
maze[row][col] = 1;
col--;
Push(&result, row, col, 1);
flagDirect = 0;
}
else{
current->direction++;
}
break;
case 4:if(row - 1 >= 0 && maze[row - 1][col] == 0){
maze[row][col] = 1;
row--;
Push(&result, row, col, 1);
flagDirect = 0;
}
else{
current->direction++;
}
break;
default:Pop(&result);
flagDirect = 0;
break;
}
}
}
show(&result);
return 0;
}


最后当我们输出栈的路径的时候,我们是从终点倒回到起点出栈输出的,这时,我们可以使用一个递归输出起点到终点

void show(Stack *result){
LNode *temp;
if(result->top->next != NULL){
temp = Pop(result);
show(result);
printf("(%d, %d)\n", temp->x, temp->y);
}
}


②使用队列来实现BFS(广度优先搜索)

队列在迷宫中的数据结构定义

/*
* 该队列为一个双重链表, 通过prev指针向前索引,但该索引关系不符合顺序关系,      next指针符合顺序关系
*/
typedef struct QNode {
int x;
int y;
struct QNode *prev;
struct QNode *next;
}QNode;
typedef struct {
QNode *front;
QNode *rear;
}Queue;


接着给出队列基本函数操作定义:

/*
* IntiQueue()
* 初始化队列,建立front指针的头结点
* rear指针一开始的时候也指向头结点
*/
void IntiQueue(Queue *result){
result->front = (QNode *)malloc(sizeof(QNode));
result->front->prev = NULL;
result->rear = result->front;
}


/*
* Push()
* 将当前坐标的结点入队,其中prev指针表示该结点的上一个坐标
* 表尾插入法就好了
*/
void Push(Queue *result, int x, int y, QNode *prev){
QNode *temp = (QNode *)malloc(sizeof(QNode));
temp->x = x;
temp->y = y;
temp->prev = prev;
temp->next = NULL;
result->rear->next = temp;
result->rear = temp;
}


给出main函数定义

int main(void){
/*
* MAX的值为5
* 定义一个二维数组
* 输入迷宫的数值,并初始化一个队列
*/
int maze[MAX][MAX];
int i, j;
for(i = 0;i < MAX;i++){
for(j = 0;j < MAX;j++){
scanf("%d", &maze[i][j]);
}
}
Queue result;
IntiQueue(&result);
/*
* row, col 为当前的横纵坐标
* (0, 0)的先前坐标是队列的头结点,也就是result.front
* find是一个哨兵,标志着是否到达终点
* current指针指向当前的坐标结点,并将当前坐标节点周围可走的坐标入队,如果       current为空的时候则表明找不到终点,队列的遍历类似于树中的层序遍历
int row = 0, col = 0;
Push(&result, row, col, result.front);
maze[row][col] = 0;
int direction = 1;
int find = 0;
QNode *current = result.front->next;
/*
* 使用顺时针遍历,能走的坐标就入队,并且为了防止重复的坐标入队,必须将走过       的路堵上
*/
while(current != NULL && !find){
direction = 1;
row = current->x;
col = current->y;
if(row == MAX - 1 && col == MAX - 1){
find = 1;
break;
}
while(direction <= 4){
switch(direction){
case 1:if(col + 1 < MAX && maze[row][col + 1] == 0){
Push(&result, row, col + 1, current);
maze[row][col + 1] = 1;
}
break;
case 2:if(row + 1 < MAX && maze[row + 1][col] == 0){
Push(&result, row + 1, col, current);
maze[row + 1][col] = 1;
}
break;
case 3:if(col - 1 >= 0 && maze[row][col - 1] == 0){
Push(&result, row, col - 1, current);
maze[row][col - 1] = 1;
}
break;
case 4:if(row - 1 >= 0 && maze[row - 1][col] == 0){
Push(&result, row - 1, col, current);
maze[row - 1][col] = 1;
}
break;
}
direction++;
}
current = current->next;
}
show(&result, current);
return 0;
}


show函数只要通过prev指针索引就能得到一条从起点走向终点的路径,当然需要递归。

void show(Queue *result, QNode *current){
if(current->prev != NULL){
show(result, current->prev);
printf("(%d, %d)\n", current->x, current->y);
}
}


该解法其实是poj 3984迷宫问题的解法,链接http://poj.org/problem?id=3984
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: