【C->数据结构与算法】->递归调用->八皇后问题

递归调用在编程中是个十分重要的部分,掌握起来也有一定的难度。本篇将用经典例题----八皇后问题,来整理递归调用的思路。

首先明确,八皇后问题是什么?
八皇后问题其中一解

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种计算机语言可以解决此问题。

即在一个8*8的棋盘上,每一行放置一个皇后,这个皇后所在的斜边竖边和横边都没有其他皇后。

先解决第一个问题,如何得到一个棋盘?
答案是二维数组。我们先建立一个行为8列为8的二维数组,每个元素的值用0和1表示,1表示有皇后,0表示无皇后,以此建立出一个棋盘。

#include <stdio.h>

#include "tyz.h"

#define   WIDTH      8

int main() {
	boolean chessboard[WIDTH][WIDTH] = {0};

	return 0;
}

tyz.h是我定义的一个头文件,里面有我经常要用到的宏和类型,boolean的本质是一个unsigned char类型,因为二维数组的元素只有0和1两种,为节省空间,使用boolean类型。
为方便修改棋盘的大小,在此将其定义成一个宏
以下为我的头文件代码,这个头文件在我的编程中会经常使用到。

#ifndef _TYZ_H_
#define _TYZ_H_

#define TRUE 1
#define FALSE 0
#define NOT_FOUND -1

typedef unsigned char boolean;

int skipBlank(const char *str);
boolean isRealStart(int ch);


#endif

定义完棋盘之后,我们需要将它显示出来,此时编写一个显示棋盘的函数。

void show(boolean (*chessboard)[WIDTH]);

void show(boolean (*chessboard)[WIDTH]) {
	int row;
	int col;
	static int count = 0;

	printf("第%d个解:\n", ++count);

	for (row = 0; row < WIDTH; row++) {
		for (col = 0; col < WIDTH; col++) {
			printf("%5d", chessboard[row][col]);
		}
		printf("\n");
	}
}

*我在此函数里定义了一个静态存储类count,用于记录show函数被调用的次数,即解的数量,*静态存储类变量不会随着函数的调用结束而释放,所以可以记录其次数

接下来思考第二个问题,每一行放置皇后时的方法一样吗?
我们可以自己画一个棋盘,然后自己放置皇后,几轮下来思考这个过程,即手工过程,我们会发现:每一行放置皇后的方法是一样的 ,认识到这一点我们才可以做出递归的思考。

从第一行开始放置皇后时,我们只需要考虑三个条件:
* 1.此行的左上方及其上方一条线上是否有皇后?
* 2.此行的正上方极其上方一条线上是否有皇后?
* 3.此行的右上方及其上方一条线上是否有皇后?
*

基于这三个条件,我们可以写出一个判断放置皇后是否安全的函数,以便在放置皇后时进行判断。

boolean isSafe(boolean (*chessboard)[WIDTH], int row, int col);

boolean isSafe(boolean (*chessboard)[WIDTH], int row, int col) {
	int i;
	int j;

	for (i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
		if (1 == chessboard[i][j]) {
			return FALSE;
		}
	}

	for (i = row - 1, j = col; i >= 0; i--) {
		if (1 == chessboard[i][j]) {
			return FALSE;
		}
	}

	for (i = row - 1, j = col + 1; i >= 0 && j < WIDTH; i--,j++) {
		if (1 == chessboard[i][j]) {
			return FALSE;
		}
	}

	return TRUE;
}

现在我们已经完成了准备工作,只剩最后一个问题就是递归调用。
要实现递归调用,先考虑递归结束条件
八皇后问题递归结束的条件是什么?是一个解法出现的时候,即从第一行到最后一行,每一行都放了一个皇后。
有了以上的分析,我们就可以开始补足其余的代码了。

递归代码如下

void chess(boolean (*chessboard)[WIDTH], int row);

void chess(boolean (*chessboard)[WIDTH], int row) {
	int col;

	if (row >= WIDTH) {
		showChess(chessboard);
		return;
	}

	for (col = 0; col < WIDTH; col++) {
		if (isSafe(chessboard, row, col)) {
			chessboard[row][col] = TRUE;
			chess(chessboard, row + 1);
			chessboard[row][col] = FALSE;
		}
	}
}

这段代码虽然不多,但是其中的递归的思想很巧妙。
如果到了某一行,皇后无法摆下去,此时这一轮函数调用结束,指令回到上一次函数调用的下一行代码,即chessboard[row][col] = FALSE; 这句代码的意思是将上一行放置的皇后取走,然后重新进行col++,再试下一个位置。

完整代码如下

#include <stdio.h>

#include "tyz.h"

#define   WIDTH      8

boolean isSafe(boolean (*chessboard)[WIDTH], int row, int col);
void showChess(boolean (*chessboard)[WIDTH]);
void chess(boolean (*chessboard)[WIDTH], int row);

void chess(boolean (*chessboard)[WIDTH], int row) {
	int col;

	if (row >= WIDTH) {
		showChess(chessboard);
		return;
	}

	for (col = 0; col < WIDTH; col++) {
		if (isSafe(chessboard, row, col)) {
			chessboard[row][col] = TRUE;
			chess(chessboard, row + 1);
			chessboard[row][col] = FALSE;
		}
	}
}

void showChess(boolean (*chessboard)[WIDTH]) {
	int row;
	int col;
	static int count = 0;

	printf("第%d个解:\n", ++count);

	for (row = 0; row < WIDTH; row++) {
		for (col = 0; col < WIDTH; col++) {
			printf("%5d", chessboard[row][col]);
		}
		printf("\n");
	}
}

boolean isSafe(boolean (*chessboard)[WIDTH], int row, int col) {
	int i;
	int j;

	for (i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
		if (1 == chessboard[i][j]) {
			return FALSE;
		}
	}

	for (i = row - 1, j = col; i >= 0; i--) {
		if (1 == chessboard[i][j]) {
			return FALSE;
		}
	}

	for (i = row - 1, j = col + 1; i >= 0 && j < WIDTH; i--,j++) {
		if (1 == chessboard[i][j]) {
			return FALSE;
		}
	}

	return TRUE;
}

int main() {
	boolean chessboard[WIDTH][WIDTH] = {0};
	chess(chessboard, 0);

	return 0;
}

运行结果如下图,共92个解
在这里插入图片描述
在这里插入图片描述

发布了10 篇原创文章 · 获赞 24 · 访问量 2461

猜你喜欢

转载自blog.csdn.net/qq_45627684/article/details/103322183
今日推荐