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;//怎么搞的哦,把top给delete了
}
}
/*************************************************/
//入栈,只要能开辟空间就能入栈,不存在栈慢的情况
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;
}