现在的位置: 首页 > 综合 > 正文

用递归和栈的方法实现N皇后求解

2018年04月06日 ⁄ 综合 ⁄ 共 12433字 ⁄ 字号 评论关闭

 

N皇后问题的栈和递归实现方式

 

八皇后问题:初始状态下,国际象棋棋盘上没有任何棋子。顺序的在棋盘上的第一行、第二行、、第八行上面布放棋子。每一行的八个位置可以放置皇后,要求任意时刻,棋盘的合法布局满足三个条件,即任何两个棋子不得摆放在棋盘上的同一行、同一列及同一对角线上。当棋盘的大小为N时,就变成了N皇后问题。解决这个问题,最简单的办法是用递归,也可以借助栈来加以实现。

用递归的办法解决N皇后问题:首先,将棋盘初始化为一个二维数组,从第0行第0列开始寻找适合的位置放置皇后。然后顺序的处理第二行、第三行、、第八行,完成一次排列。处理每一行的时候,需要循环8次来找出所有满足条件的排列。在某一行寻找适合位置的时候,需要对该行的每个位置进行安全检查,若安全则放置棋子,否则,循环后移判别下一个位置。当在第八行找到安全位置时,则一次排列探测成功,输出当前的棋盘状态,并将排列计数增加1。否则,继续在下一行进行同样的操作。就这样,直到所有的排列被穷尽搜索。

      借助栈的方法实现N皇后问题的求解:首先,同样设置一个二维字符数组表示棋盘chess的状态,设置棋盘上的每个位置结点信息(包括行列信息及是否有放过皇后true/false)。然后,设置三个一维占用记录数组,分别为:列被占用(大小为MaxSize)、正斜对角线被占用(/)、反斜对角线被占用(/),其中正反对角线占用数组均为2*MaxSize-1,将棋盘的所有结点占用信息全部初始化为false。此时,开始从第一行起处理N皇后放置问题。

       将第一列从后向前一次入栈,即chess[0][0]位于栈顶。取栈顶元素getTop(Node&node)(不是出栈操作),判别是否放过皇后:

(1)、若没有放过则往下进行:判断对角及列占用与否,若被占用则出栈进行后一位置的判别,否则,该位置放置皇后并更改结点位置信息及棋盘在该位置的状态。此时,将栈顶元素取出,并将该节点入栈。若当前行已经是最后一行,则表明一次探索成功,输出棋盘的状态,并将计数值增加1。否则,继续将下一行位置结点从后向前读进栈,重复前面的操作。

(2)、若放过,则表明前面已经成功找到n种合符要求的摆放方法,当前是在寻求新的摆放方法。因此,应将当前节点的信息初始化为未放置过皇后的状态结点,然后,继续(1)中的操作直至穷尽。

两种方法均能实现N皇后的问题求解,但是实现方式却迥然不同。递归需要很多次的自身函数调用,当N很大的时候可能造成溢出问题。栈虽然实现方式不一样,但是,当N很大的时候,仍然会需要很长的时间来进行运算,这是由于算法本身是利用穷举的原则来进行问题的求解,因此,这也是不可避免的。当N增加时,其运算复杂度可能会呈指数次的增长。当N=4时,只有2中排列方式;N=8时就有92种,而当N=10时就到了724种;当N=12时,排列总数达到了14200种之多,可见复杂度不见一般。

下面附上两种方法的C++源代码:

//递归方法

/*******************************************************

Copyright@2009-2011 by hank(SiChuan University)

*******************************************************/

//NQueen.h

#ifndef NQUEEN_H

#define NQUEEN_H

#include<string.h>

//#include<cstdlib>

//n皇后问题类定义

const int MaxSize=7;

class NQueen

{

      int count;//问题解法数

      char ChessState[MaxSize][MaxSize];//棋盘状态

      bool SafeJudge(int row,int col)/*const*/;//安全性检查

      void PlaceQueen(int row);//放置皇后  

public:

      NQueen();//构造函数

      ~NQueen(){}//析造函数

      void Solve();//解决N皇后问题

      int getCount(){return count;}//N种放置方法

      void Display();//显示

 

};

 

#endif

/******************************************************

Copyright@2009-2011 by hank(SiChuan University)

*******************************************************/

//NQueen.cpp

#include"NQueen.h"

#include<iostream>

#include<fstream>

using namespace std;

/******************************************/

//构造函数

NQueen::NQueen()

{

      count=0;

      for(int i=0;i<MaxSize;i++)

           for(int j=0;j<MaxSize;j++)

           ChessState[i][j]='-';

}

/******************************************/

//安全性检查

bool NQueen::SafeJudge(int row,int col)/*const*/

{

      int i,j;

      for(i=0;i<row;i++)//判断同列是否有Q

      {

           if(ChessState[i][col]=='Q')

                 return false;

      }

      for(j=0;j<col;j++)//判断同行是否有Q

      {

           if(ChessState[row][j]=='Q')

                 return false;

      }

      i=row-1;j=col-1;

      while(i>=0 && j>=0)//判断左上斜对角是否有Q

      //for(i=row-1,j=col-1;i>=0,j>=0;i--,j--)

      {

           if(ChessState[i][j]=='Q')

                 return false;

           i--;j--;

      }

      i=row-1;j=col+1;

      while(i>=0 && j<=MaxSize-1)//判断右上斜对角是否有Q

      //for(i=row-1,j=col+1;i>=0,j<=7;i--,j++)

      {

           if(ChessState[i][j]=='Q')

                 return false;   

           i--;j++;

      }

      return true;

 

}

/******************************************/

//放置皇后

void NQueen::PlaceQueen(int row)

{

      for(int col=0;col<MaxSize;col++)

      {

           if(SafeJudge(row,col))

           {

                 ChessState[row][col]='Q';

                 if(row<MaxSize-1)

                 {

                      PlaceQueen(row+1);

                 }

                 else

                 {

                      count++;

                      Display();

                 }

           }

            //for(int j=0;j<MaxSize;j++)

                 ChessState[row][/*j*/col]='-';

      }

}

/******************************************/

//显示

void NQueen::Display()

{

      ofstream fout("out3.txt",ios_base::app);

      //fout<<"************"<<MaxSize<<"皇后问题各种解法**********"<<endl;

      cout<<""<<count<<"种排法:"<<endl;

      fout<<"/n"<<count<<"种排法:/n"<<endl;

      for(int i=0;i<MaxSize;i++)

      {

           {

                 for(int j=0;j<MaxSize;j++)

                 {

                      cout<<ChessState[i][j]<<" ";

                      fout<<ChessState[i][j]<<" ";

                 }

           }

           cout<<endl;

           fout<<endl;

      }

      cout<<endl;    //system("pause");

      fout.close();

}

/******************************************/

//n皇后问题求解

void NQueen::Solve()

{

      PlaceQueen(0);

}

/******************************************************

Copyright@2009-2011 by hank(SiChuan University)

*******************************************************/

//main.cpp

#include"NQueen.h"

#include<iostream>

#include<fstream>

using namespace std;

 

int main()

{

      NQueen Nq;

      ofstream fout("out3.txt",ios_base::app);

      fout<<"/n/n***********************************************"<<endl;

      fout<<"               "<<MaxSize<<"皇后问题测试            "<<endl;

      fout<<"***********************************************"<<endl;

      Nq.Solve();

      fout<<MaxSize<<"皇后共有"<<Nq.getCount()<<"种解法!"<<endl;

      fout<<"/n*********************测试结束******************"<<endl;

      fout.close();

      return 0;

}

 

 

//栈实现N皇后问题

/******************************************************

Copyright@2009-2011 by hank(SiChuan University)

*******************************************************/

//Node.h

#ifndef NODE_H

#define NODE_H

//结点类定义

struct Node

{

      int row,

           col;

      bool Mark;

 

};

 

#endif

/******************************************************

Copyright@2009-2011 by hank(SiChuan University)

*******************************************************/

//LinkedStack.h

#ifndef LINKEDSTACK_H

#define LINKEDSTACK_H

//链式栈的类定义

#include<cstdlib>

#include<cassert>

 

template<class T>

struct LinkNode

{

      T data;

      LinkNode<T>* link;

      LinkNode(LinkNode<T>* ptr=NULL):link(ptr){}

      LinkNode(T item,LinkNode<T>* ptr=NULL):data(item),link(ptr){}

};

 

template<class T>

class LinkedStack

{

      LinkNode<T>* top;

public:

      LinkedStack(){top=NULL/*new LinkNode<T>*/;}//默认构造函数,初始化为NULL,怎么能是开辟空间呢!

      //LinkedStack(LinkedStack<T>& LS);//拷贝构造函数      ~LinkedStack(){makeEmpty();delete top;}//析构函数

 

      void makeEmpty();//清空栈,仅剩下栈头

      int Length();//获取栈长度

 

      LinkNode<T>* topPointer(){return top;}//取栈头指针

      bool IsEmpty()const{return top==NULL?true:false;}//空否?

      void push(T& x);//入栈

      bool pop(/*const*/ T& x);//出栈

      bool getTop(T& x)/*const*/;//取栈顶元素

      void output();//显示栈元素

      //LinkedStack<T>* Reverse();//逆转栈,用单链表逆转即可

};

#endif

/*******************************************************

Copyright@2009-2011 by hank(SiChuan University)

*******************************************************/

//LNQueen.h

#ifndef LNQUEEN_H

#define LNQUEEN_H

//用栈实现N皇后类定义

#include"Node.h"

#include"LinkedStack.h"

#include"LinkedStack.cpp"

#include<cstring>

const int MaxSize=11;

class LQueen

{

      int col[MaxSize],

           posdiog[2*MaxSize-1],

           invdiog[2*MaxSize-1];

      char chess[MaxSize][MaxSize];

      int count;

public:

      LQueen();//默认构造函数

      ~LQueen(){}//析构函数

      void LQSolve();//求解N皇后问题

      void ChessDisplay()const;//显示棋盘

      void chessInfile()const;

      int getCount(){return count;}//总的解法数

};

 

#endif

/*******************************************************

Copyright@2009-2011 by hank(SiChuan University)

*******************************************************/

//LinkedStack.cpp

#include"LinkedStack.h"

/*************************************************/

//清空栈,仅剩下栈头

template<class T>

void LinkedStack<T>::makeEmpty()

{

      LinkNode<T>* del;

      while(top!=NULL)

      {

           del=top;

           top=top->link;

           delete /*top*/del;//怎么搞的哦,把topdelete

      }

}

 

/*************************************************/

//入栈,只要能开辟空间就能入栈,不存在栈慢的情况

template<class T>

void LinkedStack<T>::push(T& x)

{

      LinkNode<T>* newNode;

      newNode=new LinkNode<T>(x);

      assert(newNode!=NULL);

      newNode->link=top;

      top=newNode;

}

/*************************************************/

//出栈,需要考虑空栈的情况

template<class T>

bool LinkedStack<T>::pop(/*const*/ T& x)

{

      if(IsEmpty())

           return false;

      LinkNode<T>* del=top;

      x=del->data;

      top=del->link;

      delete del;

      return true;

}

/*************************************************/

//取栈顶元素

template<class T>

bool LinkedStack<T>::getTop(T& x)/*const*/

{

      if(IsEmpty())

           return false;

      x=top->data;

      return true;

}

 

/*************************************************/

//获取栈长度

template<class T>

int LinkedStack<T>::Length()

{

      LinkNode<T>* current=top;

      int count=0;

      while(current!=NULL)

      {

           count++;

           current=current->link;

      }

      return count;

}

/*************************************************/

//显示栈元素,先进后出

template<class T>

void LinkedStack<T>::output()

{

      LinkNode<T>* current=top;

      if(IsEmpty())

      {

           cout<<"空栈!没有显示的元素!"<<endl;

           return;

      }

      cout<<"链式栈的元素是:"<<endl;

      while(current!=NULL)

      {

           cout<<current->data<<" ";

           current=current->link;

      }

      cout<<endl;

}

/*******************************************************

Copyright@2009-2011 by hank(SiChuan University)

*******************************************************/

//LNQueen.cpp

#include"LNQueen.h"

#include<iostream>

#include<fstream>

using namespace std;

/************************************************/

//默认构造函数

LQueen::LQueen()

{

      for(int i=0;i<MaxSize;i++)

      {

           col[i]=0;

           for(int j=0;j<MaxSize;j++)

           {

                 chess[i][j]='-';

           }

      }

      for(i=0;i<2*MaxSize-1;i++)

      {

           posdiog[i]=0;

           invdiog[i]=0;

      }

      count=0;

}

/************************************************/

//求解N皇后问题

void LQueen::LQSolve()

{

      LinkedStack<Node> Ls;

      Node node,temp;

      for(int i=0;i<MaxSize;i++)

      {

           node.row=0;

           node.col=MaxSize-1-i;

           node.Mark=false;

           Ls.push(node);

      }

      //开始处理了

      int qrow,qcol;

      while(!Ls.IsEmpty())

      {

           Ls.getTop(node)/*Ls.pop(node)*/;//此处不能是出栈,因为这样会造成回溯时发生多出栈一个元素

           qrow=node.row;

           qcol=node.col;

           if(node.Mark==false)

           {//判断列、正对角线/、反对角线/是否被占用

                 if( col[qcol] || posdiog[qrow-qcol+MaxSize-1] || invdiog[qrow+qcol])

                      Ls.pop(node);//被占用则出栈

                 else

                 {

                      col[qcol]=1;//列占用标志

                      posdiog[qrow-qcol+MaxSize-1]=1;//正对角线占用标志

                      invdiog[qrow+qcol]=1;//反对角线占用标志

                      node.Mark=true;

                      Ls.pop(temp);

                      Ls.push(node);

                      chess[qrow][qcol]='Q';

                      if(qrow==MaxSize-1)

                      {

                            count++;

                            //ChessDisplay();

                            chessInfile();//一趟排列成功,写入文件

                      }

                      else

                      {//将下一行节点读入栈进行处理

                            for(int j=0;j<MaxSize;j++)

                            {

                                  node.row=qrow+1;

                                  node.col=MaxSize-1-j;

                                  node.Mark=false;

                                  Ls.push(node);

                            }

                      }

                 }

 

           }

            else

                 {//本次排列成功,回溯!恢复当前节点为未放置过Q

                      col[qcol]=0;

                      posdiog[qrow-qcol+MaxSize-1]=0;

                      invdiog[qrow+qcol]=0;

                      chess[qrow][qcol]='-';

                      Ls.pop(node);

                 }

      }

}

/************************************************/

//显示棋盘

void LQueen::ChessDisplay()const

{

      for(int i=0;i<MaxSize;i++)

      {

           for(int j=0;j<MaxSize;j++)

                 cout<<chess[i][j]<<" ";

           cout<<endl;

      }

}

/************************************************/

//显示棋盘结果于文件之中

void LQueen::chessInfile()const

{

      ofstream fout("out.txt",ios_base::app);

      fout<<""<<count<<"种排法:"<<endl;

      for(int i=0;i<MaxSize;i++)

      {

           for(int j=0;j<MaxSize;j++)

                 fout<<chess[i][j]<<" ";

           fout<<endl;

      }

      fout<<endl;

      fout.close();

}

/******************************************************

Copyright@2009-2011 by hank(SiChuan University)

*******************************************************/

//main.cpp

#include"LNQueen.h"

#include<iostream>

#include<fstream>

using namespace std;

int main()

{

      LQueen LQ;

      cout<<"***********"<<MaxSize<<"皇后测试**********************"<<endl;

      ofstream fout("out.txt",ios_base::app);

      fout<<"/n/n***********************************************"<<endl;

      fout<<"               "<<MaxSize<<"皇后问题测试            "<<endl;

      fout<<"***********************************************"<<endl;

      LQ.LQSolve();

      fout<<MaxSize<<"皇后共有"<<LQ.getCount()<<"种解法!"<<endl;

      fout<<"/n*********************测试结束******************"<<endl;

      fout.close();

 

      cout<<"/n一共有"<<LQ.getCount()<<"种解法!各种详细排法参见out.txt/n"<<endl;

      cout<<"*********************测试结束!************"<<endl;

      return 0;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

抱歉!评论已关闭.