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

转载请注明出处。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. CodeForces Round #173 (282E) - Sausage Maximization 字典树

    练习赛的时候这道题死活超时....想到了高位确定后..低位不能对高位产生影响..并且高位要尽可能的为1..就是想不出比较好的方法了实现... 围观大神博客..http://www.cnblogs.co ...

  2. Mac下显示隐藏文件 以及修改 hosts文件内容

    修改hosts 文件内容: 进入etc 文件夹,找到hosts 文件,把该文件复制出来,修改完里面的内容后,先把etc中的hosts 文件删除,然后在把修改后的文件脱机去 可能需要管理员的密码,你输入 ...

  3. 微信jsSDK开发

    (学习类)2015年最新微信公众平台开发 微信JSSDK开发分享功能 链接地址:http://blog.163.com/sdolove@126/blog/static/1146378852015132 ...

  4. hadoop部署、启动全套过程

    Hadoop是Apache基金会的开源项目,为开发者提供了一个分布式系统的基础架构,用户可以在不了解分布式系统的底层细节的情况下开发分布式的应用,充分利用集群的强大功能,实现高速运算和存储.Hadoo ...

  5. cocos2d-x游戏开发系列教程-坦克大战游戏加载地图的编写

    上节课写了关卡选择场景,那么接下来写关卡内容,先写最基本的地图的加载 我们新建一个场景类,如下所示: class CityScene : public cocos2d::CCLayer { publi ...

  6. 1.0.2-学习Opencv与MFC混合编程之---为播放AVI视频添加滑动条

    源代码地址:http://download.csdn.net/detail/nuptboyzhb/3961642 版本1.0.2新增内容 Ø  全局变量和函数的添加: 在CVMFCview.cpp文件 ...

  7. 《火球——UML大战需求分析》(第2章 耗尽脑汁的需求分析工作)——2.1 需求分析面面观

    说明: <火球——UML大战需求分析>是我撰写的一本关于需求分析及UML方面的书,我将会在CSDN上为大家分享前面几章的内容,总字数在几万以上,图片有数十张.欢迎你按文章的序号顺序阅读,谢 ...

  8. 利用Android属性动画实现Banner的原理与实践

    事实上在Android刚推出属性动画的时候.就想利用它来设计一个Banner控件,一直没什么时间尝试. 在当时看我们应用中的Banner,使用计时器来控制自己主动播放,设置一个非常大的数,利用余数原理 ...

  9. JSP的学习(3)——语法知识二之page指令

    本篇接上一篇<JSP的学习(2)——语法知识一>,继续来学习JSP的语法.本文主要从JSP指令中的page指令,对其各个属性进行详细的学习: JSP指令: JSP指令是为JSP引擎而设计的 ...

  10. 【ASP.NET Web API教程】2.3.5 用Knockout.js创建动态UI

    原文:[ASP.NET Web API教程]2.3.5 用Knockout.js创建动态UI 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的内容 ...