八皇后问题——递归精讲

我们先来实现一个非常熟悉的递归操作——阶乘
那么,不需要多说,相信好多同学都会想到如下代码:

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;
}

我们想要运行上述代码,还是需要我们数据结构与算法专栏一直都在强调的“运用命令行或者虚拟机进行多文件联编”的方法。

至于递归运算,我们一定要进行手动的变量跟踪,递归调用的操作十分单纯,但是在我们一直递归的过程中,就可能使得逻辑看起来十分复杂,这时候就需要我们进行变量跟踪了。

编程的学习是戒骄戒躁的,若是我们不能平静心境取学习,那么,我们就很可能产生一些难以解决的问题,所以,在数据结构的学习中,我们要渐渐使得心境平和,并且在学习算法的过程中能够受到启发,我认为,这就是我们学这门课的主要目的了。

原文地址:https://www.cnblogs.com/codderYouzg/p/12411970.html