说明
该文主要为练习数据结构的栈结构,所以将会一步步实现栈结构后,再使用栈的基本功能,如建栈,出栈,入栈,而不是直接调用库函数
数据结构
- 逻辑结构: 栈
- 物理结构: 动态数组实现
算法
- 递归思想
算法描述
- 选择棋盘第一行的任意一个点作为当前点p:(x,y)
- 结束条件:栈满或是遍历完所有点
- 判断点p是否满足攻击条件:在同一行或同一列或同一斜线(正负45°),若满足转第4步,否则转第5步
- 若y<8,当前点的y++,转第2步;否则出栈,出栈点y++,作为当前点,转第3步
- 当前点入栈,x++,y=1,转第3步
代码实现思路(按顺序实现)
实现栈的结构即基本功能
定义栈结构体
实现初始化空栈
实现入栈功能
实现遍历栈功能(1.为了测试建栈与入栈功能;2.为了输出结果)
实现出栈功能利用已有的栈结构及功能实现八皇后求解
实现攻击条件是否满足功能
递归实现模拟查找功能
C++/C代码
#include<iostream>
using namespace std;
#include"stdlib.h"
#define STACK_SIZE 8
//点坐标
typedef struct
{
int x;
int y;
} Pos;
//动态数组
typedef struct
{
Pos *base;//栈底,相当于数组头指针
Pos *top;//栈底
int stacksize;//可用空间
}SqStack;
//初始化空栈
void InitStack_Sq(SqStack &S)
{
S.base = (Pos*)malloc(STACK_SIZE*sizeof(Pos));//申请空间
S.top = S.base;//栈底栈顶相同时代表栈为空
S.stacksize = STACK_SIZE;//可用空间
}
//入栈
void Push_Sq(SqStack &S, Pos pos)
{
if(S.top - S.base >= STACK_SIZE)
{
cout<<"栈满!"<<endl;
return;
}
S.top->x = pos.x;
S.top->y = pos.y;
S.top++;//更新栈顶位置
}
//出栈
Pos Pop_Sq(SqStack &S)
{
//栈空
if(S.top == S.base)
{
cout<<"栈空!"<<endl;
Pos pos;
pos.x = -1;
pos.y = -1;
return pos;
}
S.top--;
Pos pos;
pos.x = S.top->x;
pos.y = S.top->y;
//cout<<"出栈 x: "<<S.top->x<<" y: "<<S.top->y<<endl;
return pos;
}
//遍历栈
void TraverseStack_Sq(SqStack S)
{
if(S.base == NULL)
{
cout<<"栈不存在!"<<endl;
return;
}
if(S.base == S.top)
{
cout<<"栈空!"<<endl;
return;
}
//坐标输出
for(Pos *p = S.base; p<S.top; p++)
{
cout<<"x: "<<p->x<<" y: "<<p->y<<endl;
}
cout<<endl;
//图形输出
cout<<"-------------------图形验证-------------------"<<endl<<endl;
for(Pos *p = S.base; p<S.top; p++)
{
for(int j = 1; j < 9; j++)
{
if(j == p->y)cout<<"Q ";
else cout<<"_ ";
}
cout<<endl<<endl;
// cout<<"x: "<<p->x<<" y: "<<p->y<<endl;
}
}
//判断当前点是否与已有点相互攻击
bool IsAttack(SqStack S, Pos pos)
{
//栈空,即可选序列中无点
if(S.base == S.top)
{
return false;
}
//遍历:一旦位置满足攻击条件,立刻退出循环
bool bflag = false;
for(Pos *p = S.base; p < S.top; p++ )
{
//前4个条件为不在同一行、列、斜线(+-45度)
//后两个条件是为递归函数做铺垫,坐标需在合理范围内,超出该范围归为可攻击
if(p->x == pos.x || p->y == pos.y ||
(p->x - pos.x) == (p->y - pos.y) ||
(p->x + p->y) == (pos.x + pos.y) ||
pos.x > STACK_SIZE || pos.y > STACK_SIZE)
{
bflag = true;
break;
}
}
return bflag;
}
//递归寻找适合点序列
void RecursionFind(SqStack &S, Pos pos)
{
//两个递归结束条件:1.栈满(找到序列); 2.遍历完所有点
//因为是先判断递归条件,若满足才入栈。所以栈满条件的满足总是慢条件2一步
//若是将条件二改为(pos.x == STACK_SIZE && pos.y == STACK_SIZE) ,若是还未栈满,达到该条件直接就返回了,所以将缺少第STACK_SIZE行的元素
if((S.top - S.base == S.stacksize) || (pos.x == STACK_SIZE && pos.y > STACK_SIZE))return;
if(!IsAttack(S, pos))//不攻击
{
Push_Sq(S,pos);//该点入栈
// cout<<endl<<" 入栈:"<<pos.x<<endl;
if(pos.x < STACK_SIZE)//若是还有下一行,则移动到下一行递归求解
{
pos.x++;//下一行
pos.y = 1;//从第一个开始
RecursionFind(S,pos);
}
}
else//互相攻击
{
if(pos.y < STACK_SIZE)//一行没遍历完,继续遍历
{
pos.y++;
RecursionFind(S,pos);
}
else//一行已遍历完 ,说明此路不通
{
pos = Pop_Sq(S);//出栈
pos.y++;
RecursionFind(S,pos);
}
}
}
int main()
{
SqStack S;
int n;
Pos pos;
InitStack_Sq(S);
/* cout<<"请输入需要的点个数: ";
cin>>n;
for(int i = 0; i < n; i++)
{
cout<<"请输入点的横纵坐标: ";
cin>>pos.x>>pos.y;
Push_Sq(S, pos);
}
*/
// TraverseStack_Sq(S);
//起始点x必须为 1
pos.x = 1;
while(true)
{
cout<<"请选择第一行的纵坐标y (1~8):";
cin>>pos.y;
if(pos.y > 0 && pos.y < STACK_SIZE + 1)break;
}
RecursionFind(S,pos);
cout<<endl<<"--------------一种可行方案------------------- " <<endl<<endl;
TraverseStack_Sq(S);
// Pos p = Pop_Sq(S);
//cout<<"pop: x: "<<p.x<<" y: "<<p.y<<endl;
//pos.x = 100; pos.y = 1000;
//cout<<IsAttack(S, pos)<<endl;
//Pop_Sq(S);
//TraverseStack_Sq(S);
return 0;
}