【C数据结构与算法】递归调用经典例题:八皇后问题
刚刚学习了递归调用的知识,觉得递归这种算法真是太妙了。
递归算法:优点:定义清晰,容易证明其正确性和完备性;
缺点:1、资源消耗大;一个最简单的递归,其每次调用都至少消耗8B空间(这里是关于形参实参值传递的问题,详细可以参考本人的第一篇博客,https://blog.csdn.net/weixin_41978722/article/details/79921816#0-qzone-1-39788-d020d2d2a4e8d1a374a433f596ad1440),若不控制其递归深度,就很容易造成系统堆栈的溢出,使系统崩溃。
2、会出现大量重复运算。(如斐波那契数列:
int fibonacci(int n) {
return n<=2? 1:fibonacci(n-2)+fibonacci(n-1); } )
下面,通过一例题来表现一下递归算法:八皇后问题。
根据所学,递归要考虑 1.相同操作 2.思考简单 3.递归结束条件
看到问题,不要急于编程,“手工过程”很重要!如果我们面前有一个棋盘,我们要怎样摆?
1、按照一般人的思维,一行一行来;第一个皇后放到第一行第一列上(1,1),下一个皇后必然在第二行放置,放置同样从第一列开始考虑,直到该棋子安全(其左上,上,右上方向均无棋子),则开始下一行;
2、若这一行没有安全位置,就必须返回上一行,继续移动上一行的棋子找到该棋子下一个安全位置,以此类推;
3、结束条件:找到最后一行的安全位置,即第8行,结束!
所以每次递归操作只需考虑此时自己这一行就好!
那么,棋盘如何表示? 二维数组! 8*8方阵,咱们用1代表放置棋子,0代表没有放置棋子。
程序主要函数如下:
void orderQueen(int (*chessboard)[8], const int row) // 在当前行只需要考虑当前行!
{
if (row >= 8)
{
drawChessboard(chessboard);
} else {
int col;
for (col = 0; col < 8; col++) // 尝试每一列
{
chessboard[row][col] = 1; // 放置皇后
if (isSafe(chessboard, row, col)) // 若本行本列是安全的
{
orderQueen(chessboard, row + 1); // 放置下一行
}
chessboard[row][col] = 0; // 无论本行列是安全或者不安全的,都需要去掉本位置的皇后!因为,下列需要放置一个皇后!
}
}
}
void drawChessboard(int (*chessboard)[8])
{
int row;
int col;
static int count = 0;
printf("第%d个解:\n", ++count);
for(row = 0; row < 8; row++) //输出棋盘
{
for(col = 0; col < 8; col++)
{
printf("%2d", chessboard[row][col]);
}
printf("\n");
}
}
int isSafe(int (*chessboard)[8], const int row, const int col)
{
int i;
int j;
for (i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) //判断左上方向有无棋子
{
if (chessboard[i][j] == 1)
{
return 0;
}
}
for (i = row - 1, j = col; i >= 0; i--) //判断上方有无棋子
{
if (chessboard[i][j] == 1)
{
return 0;
}
}
for(i = row - 1, j = col + 1; i >= 0 && j < 8; i--, j++) //判断右上方有无棋子
{
if (chessboard[i][j] == 1)
{
return 0;
}
}
return 1;
}
如果无法理解此行判断完没有安全位置返回上一行操作的,怎么说呢?通俗点说递归他是一层套一层的,如果这层判断完自然就回到了上一层,若还是没有理解,可能在我表述不清;建议自己画棋盘用棋子对照程序 变量跟踪 ,这就很好理解了!