八皇后问题——递归精讲

logo

递归 回顾:

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

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格的国际象棋上摆放八个皇后,使其不能互相攻击,
即:

任意两个皇后不能 处于 同一行同一列同一斜线


在这里,本人就来讲解下 三个核心函数思路

八皇后问题 —— 核心函数 的实现:

首先,有 两个函数 需要我们 事先实现:

第一个是:

存放 皇后 的 棋盘 该如何实现?

答曰:

二维数组
在本题中,棋盘 就是一个 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;
}

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

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

编程的学习是戒骄戒躁的,
若是我们不能平静心境取学习,
那么,我们就很可能产生一些难以解决的问题

所以,在数据结构的学习中,我们要渐渐使得心境平和,并且在学习算法的过程中能够受到启发,我认为,这就是我们学这门课的主要目的了

posted @ 2020-03-04 20:31  在下右转,有何贵干  阅读(417)  评论(0编辑  收藏  举报