八皇后问题——递归精讲
递归 回顾:
我们先来实现一个非常熟悉的递归操作——阶乘
那么,不需要多说,相信好多同学都会想到如下代码:
int factorial(int n) {
if(n<0) {
return -1;
}
return n == 0 ? 1 factorial(n-1);
}
我们再来复习一个曾经学习C的时候编写过的一个程序——兔子繁殖问题(斐波那契额数列)
代码如下:
int Fibonacci(int n) {
if(n<0) {
return -1;
}
return n <= 2 ? 1 : Fibonacci(n-1) + Fibonacci(n-2);
}
那么,复习了递归的一些函数,我们现在来简要总结一下 递归 的 优缺点:
递归 的优缺点:
优点:
数学定义清晰,容易证明算法的正确性和完备性
缺点:
- 系统资源消耗大,每次函数的调用,都要消耗8B空间
(若不控制递归的深度,则很容易在造成“堆栈溢出”,造成程序崩溃)- 会出现 大量重复计算
现在我们就进入我们今天的主题——八皇后问题:
那么,什么是八皇后问题呢?
八皇后问题 —— 问题概述:
在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,
即:
任意两个皇后
都 不能 处于同一行
、同一列
或同一斜线
上
在这里,本人就来讲解下 三个核心函数 的 思路:
八皇后问题 —— 核心函数 的实现:
首先,有 两个函数 需要我们 事先实现:
第一个是:
存放 皇后 的 棋盘 该如何实现?
答曰:
二维数组,
在本题中,棋盘 就是一个8*8
的 二维数组
棋盘 的实现 —— drawChseeboard函数:
所以,代码如下:
void drawChseeboard(int (*chessboard)[ORDER]) {
int row;
int col;
static int count = 0; //static 类型的变量不会随着函数的调用而申请新的空间
printf("第%d个解:\n", ++count);
for(row = 0; row < ORDER; row++) {
for(row = 0; row < ORDER; row++) {
printf("%4d", chessboard[row][col]);
}
printf("\n");
}
printf("\n");
}
第二个 是:
检测 本行、本列 是否 安全
安全检测 的实现 —— isSafe函数:
这个函数相对简单,所以我们就不再进行详细的解释了,代码段如下:
boolean isSafe(int (*chessboard)[ORDER], 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 FALSE;
}
}
for (i == row - 1, j = col; i >= 0; i--) { //向上方探寻
if (chessboard[i][j] == 1) {
return FALSE;
}
}
for (i == row - 1, j = col + 1; i >= 0 && j < ORDER; i--, j++) { //向右上方探查
if (chessboard[i][j] == 1) {
return FALSE;
}
}
return TRUE;
}
那么,在以上的函数的基础上,我们就来实现下 最关键的部分 —— 放置皇后函数:
皇后安置 的实现 —— orderQueen函数:
void orderQueen(int (*chessboard)[ORDER], const int row) {
int col;
static boolean success = FALSE;
//在当前行行号已经时ORDER,意味着,前面的ORDER行已经成功!
if(!success && row == ORDER) {
success = TRUE;
drawChseeboard(chessboard);
} else {
for (col = 0; !success && col < ORDER; col++) {
chessboard[row][col] = 1; //放置皇后
if(isSafe(chessboard, row, col)) { //若本行本列是安全的
orderQueen(chessboard, row+1); //放置下一行
}
chessboard[row][col] = 0; //去掉本位置的皇后(无论本行安全不安全)
//因为,下列需要放置一个皇后!
}
}
}
以上,八皇后问题 就解决了!
那么,现在我们来总结下我们编写的程序:
八皇后问题 —— 完整代码:
boolean类型 的生成 —— mec.h:
#ifndef _MEC_H_
#define _MEC_H_
typedef unsigned char boolean;
#define TRUE 1
#define FALSE 0
#endif
代码规范 —— eightQueen.h:
#ifndef _EIGHT_QUEEN_H_
#define _EIGHT_QUEEN_H_
#include "mec.h"
#define ORDER 8
boolean isSafe(int (*chessboard)[ORDER], const int row, const int col);
void drawChseeboard(int (*chessboard)[ORDER]);
void orderQueen(int (*chessboard)[ORDER], const int row);
#endif
功能函数集 —— eightQueen.c:
#include <stdio.h>
#include "eightQueen.h"
void orderQueen(int (*chessboard)[ORDER], const int row) {
int col;
static boolean success = FALSE;
if(!success && row == ORDER) {
success = TRUE;
drawChseeboard(chessboard);
} else {
for (col = 0; !success && col < ORDER; col++) {
chessboard[row][col] = 1;
if(isSafe(chessboard, row, col)) {
orderQueen(chessboard, row+1);
}
chessboard[row][col] = 0;
}
}
}
void drawChseeboard(int (*chessboard)[ORDER]) {
int row;
int col;
static int count = 0;
printf("第%d个解:\n", ++count);
for(row = 0; row < ORDER; row++) {
for(row = 0; row < ORDER; row++) {
printf("%4d", chessboard[row][col]);
}
printf("\n");
}
printf("\n");
}
boolean isSafe(int (*chessboard)[ORDER], 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 FALSE;
}
}
for (i == row - 1, j = col; i >= 0; i--) {
if (chessboard[i][j] == 1) {
return FALSE;
}
}
for (i == row - 1, j = col + 1; i >= 0 && j < ORDER; i--, j++) {
if (chessboard[i][j] == 1) {
return FALSE;
}
}
return TRUE;
}
还有就是我们调用这些函数的主函数了:
测试 —— youzgTest.c:
#include <stdio.h>
#include "eightQueen.h"
int main() {
int chess[ORDER][ORDER] = {0};
orderQueen(chess, 1);
return 0;
}
我们想要运行上述代码,还是需要我们数据结构与算法专栏一直都在强调的 运用 命令行 或者 虚拟机 进行 多文件联编
至于递归运算,我们一定要进行手动的变量跟踪,递归调用的操作十分单纯,
但是在我们一直递归的过程中,就可能使得逻辑看起来十分复杂,这时候就需要我们进行 变量跟踪
了
编程的学习是戒骄戒躁的,
若是我们不能平静心境取学习,
那么,我们就很可能产生一些难以解决的问题
所以,在数据结构的学习中,我们要渐渐使得心境平和,并且在学习算法的过程中能够受到启发,我认为,这就是我们学这门课的主要目的了