本节主要讲八皇后问题的基本规则和递归回溯算法的实现以及具体的代码实现和代码分析。

转载请注明出处。http://write.blog.csdn.net/postedit/10813257

一、八皇后问题和递归回溯算法

1.八皇后是一个递归回溯算法的典型问题,问题的由来是这样的,在国际象棋中有8*8个位置,那么我们有8个皇后,我们要把8个皇后分别放在不同的行,不同的列和不同的对角线上,也就是说我们要让这8个皇后不能相互攻击。

2.八皇后问题最好的解决办法是回溯算法,回溯算法的基本思路如下:

①从问题的某一状态出发,搜索可以到达所有状态②当某个状态到达后,可向前回退,并继续搜索其他可达状态③当所有状态都到达后,回溯算法结束

二、八皇后问题的基本解决思路

1.我们首先对棋盘进行规定,为了方便查看我们将棋盘定义边框。因为原来真正可以安置皇后的位置是8*8,再加上棋盘的边框,那么我们就编程了10*10的棋盘了,这样我们可以来定义一个二维数组数组来描述棋盘,这里我们将描述棋盘的二维数组定义如下:

board[10][10];

2.棋盘定义结束后,我们要打印出棋盘的边框,同时真正的8*8棋盘的位置是要空出来的,因为这个时候我们还没有进行回溯算法,所以我们根本不确定到底在哪个位置安放皇后。棋盘的初始化函数我们定义为init(),具体实现代码如下:

void init ()
{ int i = 0;
int j = 0;
for (i = 0; i < 10; i++)
{
board[0][i] = '#';
board[i][0] = '#';
board[i][9] = '#';
board[9][i + 1] = '#';
}
for (i = 1; i <= 8; i++)
{
for (j = 1; j <= 8; j++)
{
board[i][j] = ' ';
}
}
}

在棋盘的初始化代码中,我们注意点是:

board[0][i] = '#';
board[i][0] = '#';
board[i][9] = '#';
board[9][i] = '#';

这里我们实际上分别对二维数组的第0行的每一个位置置为‘#’,然后对二维数组的第0列的每一个元素置为‘#’,再对二维数组的第10列中的每一个元素置为‘#’,最后对二维数组的第十行中的每一个元素置为‘#’。

第二个for循环的作用就是把这个棋盘所在的二维数组中的其他元素都置为空,菜鸟就是菜鸟,我在手敲代码的时候居然忽略了数组的开闭空间问题,所以导致打印出错。

3.对棋盘初始化结束后,我们为了方便以后的测试,这里我们先写一下打印函数,打印函数用于打印数组的每一个元素,这里将打印函数定义为output。具体代码如下:

void output()
{
int i = 0;
int j = 0;
for (i = 0; i < 10; i++)
{
for (j = 0; j < 10; j++)
{
printf ("%c", board[i][j]);
}
printf ("\n");
}
}

在打印函数中需要注意的是每一打印完数组中的一行内容以后,我们都要打印一个换行,要不然我们是绝对看不到棋盘的形状的。

4.两个外围函数写完了以后也就真正的进入了主角函数,主角函数用来查找那些位置可以放置皇后。将主角函数命名为find(),具体的实现代码如下:

void find(int i)
{
int ret = 0;
int j = 1; if (i > 8)
{
output();
getchar();
}
else
{
for (j = 1; j <= 8; j++)
{
if (check (i, j))
{
board[i][j] = '*';
find(i + 1);
board[i][j] = ' ';
}
}
}
}

首先我们队传进来的i的值进行判定,当然i的值通常都为1,因为我们都是要从1开始进行遍历的,然后我们开始进行遍历,进入for循环,进行递归和回溯算法。这里的具体程序执行步骤我会在下面继续说,这里暂且不提。

5.在上面的find()函数中,我们会发现,我们调用了一个check()函数,这个函数的主要作用是用来判定我们当前元素的位置是否合法,然后把判定的结果返回给find,供find在if判定中使用。下面是check函数的源代码:

int check (int i, int j)
{
int ret = 1;
int k = 0; for (k = 0; k < 3; k++)
{
int ni = i;
int nj = j; while (ret && (board[ni][nj] != '#'))
{
ni = ni + pos[k].heng;
nj = nj + pos[k].zong; if (board[ni][nj] == '*')
{
ret = 0;
}
else
{
ret = 1;
}
}
}
return ret;
}

在check函数中,我们首先进入for循环,当然变量k的定义主要是用来作为循环变量了。返回值ret先定义为1,如果可以放置则返回1,如果不可以放置则返回0。我们每一次进入for循环都将传进来的i,j的值赋给ni,nj,这样在进入while中时候我们可以对位置坐标进行偏移操作,我们通过for循环和while大循环可以还有坐标的移位操作可以对当前元素所在的位置相对于纵向所在的线,对角线所在的线。这里通过传进来的元素位置来确定偏移的原理图如下:

假定我们向check函数传递的是i,j.这样我们需要遍历的方向是红色箭头所指的三个方向,分别有①,②,③,④,⑤,⑥三个位置需要遍历,查看是否有*全部都没有星号,那么我们最后向find函数返回1,如果有一个位置有星号,我们向find函数返回0.

因为我们定义的是二维数组board[8][8]的棋盘,所以可以知道①的坐标是(i-1,j-1),②的坐标是(i-2,j-2),③的坐标是(i-1,j),④的坐标是(i-2,j),⑤的坐标是(i-1,j+1),⑥的坐标是(i-1,j+2),因为在check函数中我们使用的是while循环来控制坐标偏移的,代码如下:

while (ret && (board[ni][nj] != '#'));

这样我们不遇见#这个while循环不会终止,所以我们可以使用一个二维数组来控制这个偏移量,为了代码的阅读,我们同时还将偏移量的具体变量定义放在了一个结构体中。代码如下:

typedef struct _tag_pos
{
int heng;
int zong;
}poss;
poss pos[] = {{-1, -1}, {-1, 0}, {-1, 1}} ;

通过上面的定义我们会发现我们执行while大循环,我们都会将现有的坐标位置来和坐标的偏移量进行相加,直至棋盘的边框位置,这样就完成了红色箭头的三个方向所有位置的遍历。

再来说一下,check函数的执行机制,因为主要是两个循环语句在控制,所以这里做了一个excel表格,截图传上来,方便观看。

6.完成了每一个位置的合法性判定以后,我们再来回头看find函数,find函数是真正的递归回溯算法的实现。

我们这里以程序的第一轮判定为例进行说明:

find函数调用check函数并依次执行for循环,以行为单位将皇后安排在合法的位置上。程序指定到第6次调用find函数的时候,通过检测发现已经在第六行已经没有合法的位置的可以安排皇后了,这样find(6)函数返回到函数开始执行的位置,顺序向下执行,执行语句board[i][j] = ' ';这条语句的作用是把第五行的所有位置清空,然后程序继续返回到find(5)函数中执行,重新遍历,然后再依次执行,这样完成了回溯算法的实现。

当每次执行完find(8)函数后程序都会返回到上一层函数,然后再进行依次执行,直到递归调用的结束。

来个动图嗨皮一下,顺便脑海里想着它的遍历的历程。

三、八皇后问题的代码实现

#include <stdio.h>

typedef struct _tag_pos
{
int heng;
int zong;
}poss; /*定义偏移量的数组*/
poss pos[] = {{-1, -1}, {-1, 0}, {-1, 1}} ; /*用来描述棋盘的数组*/
static char board[10][10]; /***********************************************************************************************
*函数名: init
*参数:void
*返回值:void
*功能:创建棋盘 同时创建边界
***********************************************************************************************/
void init ()
{ int i = 0;
int j = 0;
for (i = 0; i < 10; i++)
{
board[0][i] = '#';
board[i][0] = '#';
board[i][9] = '#';
board[9][i] = '#';
}
for (i = 1; i <= 8; i++)
{
for (j = 1; j <= 8; j++)
{
board[i][j] = ' ';
}
}
} /***********************************************************************************************
*函数名:output
*参数:void
*返回值:void
*功能:输出棋盘中每一个元素
***********************************************************************************************/
void output()
{
int i = 0;
int j = 0;
for (i = 0; i < 10; i++)
{
for (j = 0; j < 10; j++)
{
printf ("%c", board[i][j]);
}
printf ("\n");
}
} /***********************************************************************************************
*函数名:check
*参数:void
*返回值:合法返回1,不合法返回0
*功能:传进来的是现在位置,然后根据偏移量,判定这个位置是否合法
***********************************************************************************************/
int check (int i, int j)
{
int ret = 1;
int k = 0; for (k = 0; k < 3; k++)
{
int ni = i;
int nj = j; while (ret && (board[ni][nj] != '#'))
{
ni = ni + pos[k].heng;
nj = nj + pos[k].zong; if (board[ni][nj] == '*')
{
ret = 0;
}
else
{
ret = 1;
}
}
}
return ret;
} /***********************************************************************************************
*函数名:find
*参数:void
*返回值:int i 从主函数传进来的参数,用于规定从哪一行开始
*功能:查找共有多少种方法
***********************************************************************************************/
void find(int i)
{
int ret = 0;
int j = 1; if (i > 8)
{
output();
getchar();
}
else
{
for (j = 1; j <= 8; j++)
{
if (check (i, j))
{
board[i][j] = '*';
find(i + 1);
/*
假如上面的find(i + 1)调用以后,执行到if (check (i, j) )
这个时候不满足条件,程序跳出,然后find(i + 1)返回,因为这个位置的条件不满足,
所以程序继续向下执行,在for循环里继续循环,直到递归到最里层。然后每一个函数在
返回的时候都会执行board[i][j] = ' '
*/
board[i][j] = ' ';
}
}
}
} int main()
{
init();
find(1);
return 0;
}

C语言数据结构----递归的应用(八皇后问题的具体流程)的更多相关文章

  1. C语言数据结构----递归的应用(斐波拉契数列、汉诺塔、strlen的递归算法)

    本节主要说了递归的设计和算法实现,以及递归的基本例程斐波拉契数列.strlen的递归解法.汉诺塔和全排列递归算法. 一.递归的设计和实现 1.递归从实质上是一种数学的解决问题的思维,是一种分而治之的思 ...

  2. C语言:试探算法解决“八皇后”问题

    #include <stdio.h> #define N 4 int solution[N], j, k, count, sols; int place(int row, int col) ...

  3. C#中八皇后问题的递归解法——N皇后

    百度测试部2015年10月份的面试题之——八皇后. 八皇后问题的介绍在此.以下是用递归思想实现八皇后-N皇后. 代码如下: using System;using System.Collections. ...

  4. java递归求八皇后问题解法

    八皇后问题 八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例.该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处 ...

  5. 八皇后问题详细分析与解答(递归法解答,c#语言描述)

    八皇后问题,是一个古老而著名的问题,是回溯算法的典型例题.该问题是十九世纪著名的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或 ...

  6. 八皇后问题 递归实现 C语言 超详细 思路 基础

    八皇后问题 :假设 將八个皇后放到国际象棋盘上,使其两两之间无法相互攻击.共有几种摆法? 基础知识: 国际象棋里,棋盘为8X8格. 皇后每步可以沿直线.斜线 走任意格. 思路: 1.想把8个皇后放进去 ...

  7. 数据结构之递归Demo(走迷宫)(八皇后)(汉诺塔)

    递归 顾名思义,递归就是递归就是递归就是递归就是递归......就是递归 Google递归:

  8. C#数据结构与算法系列(十四):递归——八皇后问题(回溯算法)

    1.介绍 八皇后问题,是一个古老而著名的问题,是回溯算法的经典案例,该问题是国际西洋棋棋手马克斯.贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即 任意两个皇后都不能处 ...

  9. 7, java数据结构和算法: 八皇后问题分析和实现 , 递归回溯

    什么是八皇后问题: 指的是,在一个8 * 8的棋盘中, 放置8个棋子, 保证这8个棋子相互之间, 不在同一行,同一列,同一斜线, 共有多少种摆法? 游戏连接: http://www.4399.com/ ...

随机推荐

  1. java模拟实现生产者---消费者问题

    本文章为小编原创,请尊重文章的原创性,转载请注意写明转载来源:http://blog.csdn.net/u012116457 已知技术參数: 生产者消费者问题,描写叙述一组生产者向一组消费者提供产品/ ...

  2. Eclipse用法和技巧二十六:浅谈快捷键

    网络上到处都是eclipse有哪些常用的快捷键,其中还有很多讲得着实不错,这里就不再狗尾续貂而是谈谈别的这段时间的一些思考.最近加入了开发团队,代码量突突的上去了,同时也发现关于快捷键还是有很多细节, ...

  3. Delphi体系内部的4种消息传递办法(Send,Post,Perform,Dispatch)

    一.什么是消息? 消息是windows对应用程序发送的有关‘发生了某种事件’的通知.例如点击鼠标,调整窗口大小或键盘上按下一个键,都会引起windows发送一条消息到应用程序中去,去通知应用程序发生了 ...

  4. 杂题_POJ上的过桥问题

    本文出自:http://blog.csdn.net/svitter 过桥问题解释:一条船能够坐两个人,可是有非常多人要过河,所以送过一个人去,还有一个人还要回来接.使全部人过河之后时间最短,怎样求? ...

  5. Qt Creator键盘快捷键速查

    原地址:http://bbs.qter.org/forum.php?mod=viewthread&tid=904&extra=page%3D2 一般操作的键盘快捷键 操作 快捷键 操作 ...

  6. Kendo UI开发教程(25): 单页面应用(三) View

    View为屏幕上某个可视部分,可以处理用户事件. View可以通过HTML创建或是通过script元素.缺省情况下View将其所包含的内容封装在一个Div元素中.Kendo创建View有两种方式: 使 ...

  7. Swift编程语言学习1.4——数值型字面量、数值类型转换

    数值型字面量 整数字面量能够被写作: 一个十进制数,没有前缀 一个二进制数,前缀是0b 一个八进制数,前缀是0o 一个十六进制数,前缀是0x 以下的全部整数字面量的十进制值都是17: let deci ...

  8. 类CL_ABAP_TYPEDESCR,动态取得运行时类型

    有时候我们要在程序运行的时候取得某个内表或者某个结构它的属性或者它的字段的属性,可能通过类CL_ABAP_TYPEDESCR和它的子类取得指定内表的属性.类CL_ABAP_TYPEDESCR和它的子类 ...

  9. 有关java中的final关键字

    在java中,可能使用到final关键字修饰的有数据.方法和类. 一.final 修饰数据 有final修饰的数据是用来告诉编译器一块数据是恒定不变的,有时数据恒定不变是很有用的,比如: 1.一个永不 ...

  10. 管道是如何处理HTTP请求的?

    管道是如何处理HTTP请求的? 我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但是就具体的实现来说,由于其中涉及很多对象的交互,我想很 ...