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