(本文的所有代码均是基于此文:http://blog.csdn.net/mbh_1991/article/details/23869459,感谢博主的贡献!)
最近看了一篇文章(见上面给出的链接),里面讲到了回溯算法和八皇后问题。仔细阅读全文之后,发现作者所写与实际开发工作还是有一定的差别,因此特发此文,表达一下个人的看法,请各位批评指正。
什么是回溯算法?举个例子来说,当你走到一个有很多岔路的路口,不知道哪条路是通的,于是,你随便选择了一条,当走到路的尽头发现无路可走时,便回过头来选择另外的一条路走,直到走通某条路为止。有关回溯和八皇后问题的详细描述,请见原文。
在回溯算法中,利用了递归的思想。但是,在实际的软件开发项目中,我们要慎用递归来编写代码,原因是:
第一,我们日常写代码编函数有时并不确定是谁什么时候来调用,所以递归一般不用。教科书上的东西教的是方法和理论,用来做单项训练是很有必要的,但是实际工作中考虑的因素就多得多,可用性、可测试性、可移植性、高性能、规范性等等都很重要。因为产品研发总是算成本的,但是如果有短板迟早会付出代价的。
第二,如果递归的次数过多,会造成程序栈的耗尽。同时,要限制递归使用的范围,防止出现异常。实际上所有递归都可以用非递归方式实现,并且一般非递归的效率更高。
好了,别的不说了。本人对原文中的代码进行了一定的改进,最终代码如下所示:
/**********************************************************************
*版权所有 (C)2014, Zhou Zhaoxiong。
*
*文件名称: Eight.c
*文件标识:无
*内容摘要:用于实现八皇后问题
*其它说明:无
*当前版本: V1.0
*作 者: Zhou Zhaoxiong
*完成日期: 20140418
*
*修改记录1://修改历史记录,包括修改日期、版本号、修改人及修改内容
*修改日期:
*版本号:
*修改人:
*修改内容:
*
**********************************************************************/
#include <stdio.h>
typedef unsigned char UINT8;
typedef unsigned int UINT32;
typedef signed int INT32;
typedef void VOID;
typedef UINT8 BOOL;
#define QueenNum 8 //八皇后
#define TRUE (BOOL)1
#define FALSE (BOOL)0
UINT8 g_szBoard[QueenNum+2][QueenNum+2] = {0};
UINT32 g_ConditionCount = 0; //记录八皇后的情况个数
typedef struct _tag_pos //定义一个数据结构来充当方向
{
UINT32 iHorizontal;
UINT32 iVertical;
}TPos;
//检测三个方向:左上,右上,正上(横排是不检测的,因为一排只放一个)
TPos tPos[3] = {{-1,-1}, {-1,1}, {-1,0}};
//函数声明
VOID InitBoard();
VOID DisplayBoard(UINT32 iHorizontal, UINT32 iVertical);
BOOL CheckBoard(UINT32 iHorizontal, UINT32 iVertical);
VOID FindBoard(UINT32 iLine, UINT32 iHorizontal, UINT32 iVertical);
INT32 main();
/**********************************************************************
*功能描述:初始化棋盘
*输入参数: 无
*输出参数:无
*返回值:无
*其它说明:无
*修改日期 版本号 修改人 修改内容
* ------------------------------------------------------------------------------------------------------
* 20140418 V1.0 zzx 创建
***********************************************************************/
VOID InitBoard()
{
UINT32 iHorizontal = 0;
UINT32 iVertical = 0;
for (iHorizontal = 0; iHorizontal < QueenNum + 2; iHorizontal ++)
{
g_szBoard[0][iHorizontal] = '#';
g_szBoard[QueenNum+1][iHorizontal] = '#';
g_szBoard[iHorizontal][0] = '#';
g_szBoard[iHorizontal][QueenNum+1] = '#';
}
for (iHorizontal = 1; iHorizontal <= QueenNum; iHorizontal ++)
{
for (iVertical = 1; iVertical <= QueenNum; iVertical ++)
{
g_szBoard[iHorizontal][iVertical] = ' ';
}
}
}
/**********************************************************************
*功能描述:打印棋盘内容
*输入参数: iHorizontal-水平位置
iVertical-垂直位置
*输出参数:无
*返回值:无
*其它说明:无
*修改日期 版本号 修改人 修改内容
* ------------------------------------------------------------------------------------------------------
* 20140418 V1.0 zzx 创建
***********************************************************************/
VOID DisplayBoard(UINT32 iHorizontal, UINT32 iVertical)
{
UINT32 iLoopOuter = 0;
UINT32 iLoopInner = 0;
if (g_szBoard[iHorizontal][iVertical] == '*')
{
for (iLoopOuter = 0; iLoopOuter < QueenNum + 2; iLoopOuter ++)
{
for (iLoopInner = 0; iLoopInner < QueenNum + 2; iLoopInner ++)
{
printf("%c ", g_szBoard[iLoopOuter][iLoopInner]);
}
printf("\n");
}
}
}
/**********************************************************************
*功能描述:检查是否可以放皇后
*输入参数: iHorizontal-水平位置
iVertical-垂直位置
*输出参数:无
*返回值: TRUE-可以放置皇后 FALSE-不能放置皇后
*其它说明:无
*修改日期 版本号 修改人 修改内容
* ------------------------------------------------------------------------------------------------------
* 20140418 V1.0 zzx 创建
***********************************************************************/
BOOL CheckBoard(UINT32 iHorizontal, UINT32 iVertical)
{
UINT32 iPos = 0; //表示方向
BOOL bRetFlag = TRUE;
for (iPos = 0; iPos < 3; iPos ++) //检测三个方向
{
UINT32 iHorizontalNew = iHorizontal;
UINT32 iVerticalNew = iVertical;
while (bRetFlag && (g_szBoard[iHorizontalNew][iVerticalNew] != '#')) //判断有没有到达棋盘边界
{
iHorizontalNew = iHorizontalNew + tPos[iPos].iHorizontal;
iVerticalNew = iVerticalNew + tPos[iPos].iVertical;
bRetFlag = bRetFlag && (g_szBoard[iHorizontalNew][iVerticalNew] != '*'); //判断这个方向有没有放过皇后
}
}
return bRetFlag; //可以放皇后返回TRUE,不可返回FALSE
}
/**********************************************************************
*功能描述:查找皇后的放置位置并放置皇后
*输入参数: iLine-行位置
iHorizontal-水平位置
iVertical-垂直位置
*输出参数:无
*返回值:无
*其它说明:无
*修改日期 版本号 修改人 修改内容
* ------------------------------------------------------------------------------------------------------
* 20140418 V1.0 zzx 创建
***********************************************************************/
VOID FindBoard(UINT32 iLine, UINT32 iHorizontal, UINT32 iVertical)
{
UINT32 iRow = 0;
if (iLine > QueenNum) //判断是否已经超过了第八行
{
if (g_szBoard[iHorizontal][iVertical] == '*') //当该位置处有皇后时,才打印棋盘
{
g_ConditionCount ++; //计算八皇后情况的个数
DisplayBoard(iHorizontal, iVertical);
}
}
else
{
for (iRow = 1; iRow <= QueenNum; iRow ++) //判断一行是否有匹配的位置
{
if (CheckBoard(iLine, iRow))
{
g_szBoard[iLine][iRow] = '*'; //放置皇后
FindBoard(iLine+1, iHorizontal, iVertical);
g_szBoard[iLine][iRow] = ' '; //清除放错的皇后
}
}
}
}
/**********************************************************************
*功能描述:主函数
*输入参数: 无
*输出参数:无
*返回值: 0-执行完毕
*其它说明:无
*修改日期 版本号 修改人 修改内容
* ------------------------------------------------------------------------------------------------------
* 20140418 V1.0 zzx 创建
***********************************************************************/
INT32 main()
{
UINT32 iHorizontal = 0; //皇后位置的行号
UINT32 iVertical = 0; //皇后位置的列号
printf("请输入某皇后所在位置的行号和列号(中间用空格分隔): \n");
scanf("%d %d", &iHorizontal, &iVertical);
InitBoard(); //初始化棋盘
printf("棋盘示意图如下: \n");
FindBoard(1, iHorizontal, iVertical); //查找特定位置有皇后的所有情况,注意:第一个参数为1
printf("某皇后的行号=%d,列号=%d的情况总数为:%d.\n", iHorizontal, iVertical, g_ConditionCount);
return 0;
}
与原来的代码相比,修改之后的代码有如下变化:
第一,对原代码进行了重新排版和规范化,添加了注释,规范了变量和函数的命名,添加了更多的打印消息提示。
第二,将之前的“void find(int i)”修改为“VOID FindBoard(UINT32 iLine, UINT32 iHorizontal, UINT32 iVertical)”,添加了两个参数“iHorizontal”和“iVertical”,用于打印在某位置处有皇后的情况的总数。原代码将所有情况都打印出来了,这样不方便对输出结果进行检查。
第三,原代码里面有条语句“if(check(i,j))”,这样写是不规范的。check函数的返回值为int型,而该if语句将函数的返回值当成了bool型。因此,在修改之后的代码中,将CheckBoard函数的返回值定义为bool型的,符合函数调用的规范。
第四,在调用函数之前,要对它们进行声明,即使被调用函数的实现语句在调用函数的实现语句的前面,这也是规范性的体现。
程序的执行结果如下图所示:
“他山之石,可以攻玉”,我们要不断地向他人学习,才能够及时地发现自身的不足,也才能够提升自身的能力。
(本人新浪微博:http://weibo.com/zhouzxi?topnav=1&wvr=5,微信号:245924426,欢迎关注!)