迷宫求解问题来源自”数据结构(C语言版)“一书第50页的例题。该例题要求在不使用递归(因为暂时还没讲到),只能通过使用诸如入栈出栈的方式获取一条可以走出迷宫的路径。
在看完文字提示后,我就没有看后面的伪代码实现了(对于我来说,本书的所有伪代码的组织就像一团乱麻,反而更加没有头绪)。在理解文字说明的基础上我试图通过独立思考解决,以下就是我的思考过程。
1.迷宫求解问题的规则有哪些?
- 走出迷宫的路径(以下简称“路径”)是从迷宫起点至迷宫终点(这简直就是一句废话)
- 路径不能重复(通俗点讲,就是说该条路径的路线不能交叉;如果用多个点来表示路径中所有位置,那么这个点的集合中没有相同位置的两个点)
- 路径指向沿着上下左右的方向前进,不能斜向前进
- 遇到障碍物时无法前进(又是一句废话)
2.关于迷宫、迷宫中的障碍(墙)以及路径如何表示?
- 迷宫显然是采用一个二维数组来表示,数组的行列数的组合即代表迷宫中的每一个组成块的位置。用这个二维数组中每一个元素的存储值表示对应组成块的属性(空格或者是墙体)。
- 根据玩迷宫游戏的常识,探索路径是在不断前进与后退的过程中完成的,因此采用栈结构存储该路径中的每一个代表属于路径的组成块(以下简称为“通道块”)。
- 路径中每一个位置的前进方向采用北、东、南、西四个标识符表示。如果在寻路过程中发现某一个通道块的四个方向都不能走,则此时该通道块属于“死路一条”,使用另外一个标识符表示之。
3.结合第1,2步的思考过程,我制定了求解迷宫路径的大概规则:
- 创建一个二维数组代表迷宫,该数组只能存储两个值,分别代表墙体和可行进的空间。
- 创建一个栈结构,存储在探索迷宫出口过程中的行进路线;当某一位置可以行进时将代表该位置的通道块入栈,如果该位置的四个方向都没办法行进时,执行出栈操作。
- 用”north/east/south/west/no_road”表示路径中每一个通道块下一步的走向;其中no_road表示通道块“无路可走”的状态。每次路径的探索从north开始,沿着顺时针方向至west,对应的下一个通道块如果可行进则在该方向上前进一步;四至均不通则标识为no_road,从栈结构中退出该通道块(这一过程简称为“下一个通道块位置”的判断,属于迷宫求解问题的核心部分)。
- 在判断“下一个通道块位置”的过程中,不仅要考虑下一个通道块是墙体的情况;为保证路线不交叉,还需要确保在之前的路径探索中没有“路过”该位置。
- 该迷宫没有正确路径情况下对应的条件:代表起点的通道块被标志为“无路可走”(no_road)。
- 已经找到一条正确路径对应的条件:最后一个入栈的通道块抵达了迷宫终点的位置。
4. 代码的组织
采用c-free完成代码的编写,文件结构如上图所示;其中,content的文件实现了栈的基本操作,包括入栈出栈,返回栈顶元素,查找特定数据是否存在于栈中。mazeoperation文件实现了迷宫求解的方法;main文件顾名思义是程序入口。
5.核心代码的实现
迷宫求解的核心方法有两个:is_next_block_pass方法以及get_maze_path方法。
5.1 is_next_block_pass方法
is_next_block_pass方法是根据当前路径中最后一个通道块的位置与前进方向获取下一个通道块的位置,并判断该通道块是否为墙体(障碍)或者是重复的路径。代码如下:
(在这里有一点需要说明,由于初学C语言,因此在信息隐藏方面做得不是很好,因此主要的变量均采用了外部变量的声明方式:curr_block表示栈顶元素代表的通道块;next_block则表示根据curr_block所计算的下一个通道块,start_block和end_block分别表示起点和终点所在的通道块————这种方法非常的不好,请大家不叫见笑)
Bool is_next_block_pass(void)
{
Ordinate target_ord;<span style="white-space:pre"> </span>//当前通道块的前进方向
int next_block_x, next_block_y;<span style="white-space:pre"> </span>//下一个通道块
Bool is_block_exist;<span style="white-space:pre"> </span>//标志下一个通道块是否已经存在于路径链表中 TRUE表示已经存在
target_ord = curr_block->ord;<span style="white-space:pre"> </span>//当前路径中最后一个通道块指向下一个的方向
next_block = malloc(sizeof(struct mazeblock));
if(next_block == NULL){
printf("Error, malloc is failed.\n");
exit(EXIT_FAILURE);
}
switch(target_ord){<span style="white-space:pre"> </span>//根据前进方向,计算下一个通道块的位置
case north:
next_block->seat.x = (curr_block->seat.x) - 1;
next_block->seat.y = curr_block->seat.y;
break;
case east:
next_block->seat.x = curr_block->seat.x;
next_block->seat.y = (curr_block->seat.y) + 1;
break;
case south:
next_block->seat.x = (curr_block->seat.x) + 1;
next_block->seat.y = curr_block->seat.y;
break;
case west:
next_block->seat.x = curr_block->seat.x;
next_block->seat.y = (curr_block->seat.y) - 1 ;
break;
}
next_block_x = next_block->seat.x;
next_block_y = next_block->seat.y;
is_block_exist = find_block(next_block);<span style="white-space:pre"> </span>//查询特定位置的通道块是否存在于栈中
if(maze[next_block_x][next_block_y] == EXIST || is_block_exist == TRUE){<span style="white-space:pre"> </span>//当下一个通道块是障碍或者已经存在于路径中时,表示无法向该方向前进
free(next_block);
return FALSE;
}else{
return TRUE;
}
}
5.2 get_maze_path方法
get_maze_path方法是通过一个循环实现迷宫通道的获取;代码如下:
void get_maze_path(void)
{
//思路:将起点通道块入栈,然后将栈中栈顶元素标记为curr_block进入循环中,根据curr_block的ord得到next_block,判断并执行相应操作
Bool next_status;<span style="white-space:pre"> </span>//存储么一次执行<span style="font-family: SimSun;">is_next_block_pass函数的返回值</span>
init_contents(); //初始化一个栈
push(start_block); //将起点通道块入栈
while(start_block->ord != no_road){ //退出循环的条件是当起点通道块的ord为no_road,此时表示起点通道块已经被验证为“无路可走”
curr_block = get_top_value();
if(curr_block->seat.x == 8 && curr_block->seat.y == 8){<span style="white-space:pre"> </span>//当curr_block代表的通道块抵达终点时,跳出循环
break;
}
if(curr_block->ord == no_road){<span style="white-space:pre"> </span>//当curr_block的ord值为no_road时,无路可走;此时执行以下步骤
pop();<span style="white-space:pre"> </span>//1.执行出栈操作
curr_block = get_top_value();<span style="white-space:pre"> </span>//2.重新为当前通道块赋值
(curr_block->ord)++;<span style="white-space:pre"> </span>//3.将当前通道块的前进方向自增(沿顺时针指向下一个方向)
continue;<span style="white-space:pre"> </span>//4.跳过循环中的剩余部分,执行下一次循环操作
}
next_status = is_next_block_pass();<span style="white-space:pre"> </span>//获取下一个通道块是否可以前进的判断状态
if(next_status == TRUE){ <span style="white-space:pre"> </span>//下一个通道块可以前进,则将初始化该通道块的起始方向并执行入栈操作
next_block->ord = north;
push(next_block);
continue;
}else{
(curr_block->ord)++;<span style="white-space:pre"> </span>//下一个通道块无法前进,将当前通道块的前进方向自增
}
}
6.打印输出
最终打印输出效果如下图所示:
7.程序源代码链接
程序的源文件已经上传,地址为:http://download.csdn.net/detail/u010676110/8688027;有兴趣的朋友可以看一看,欢迎理性的批评指正。