八皇后--棧遞迴實現
阿新 • • 發佈:2018-12-20
說明
該文主要為練習資料結構的棧結構,所以將會一步步實現棧結構後,再使用棧的基本功能,如建棧,出棧,入棧,而不是直接呼叫庫函式
資料結構
- 邏輯結構: 棧
- 物理結構: 動態陣列實現
演算法
- 遞迴思想
演算法描述
- 選擇棋盤第一行的任意一個點作為當前點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;
}