1. 程式人生 > >八皇后--棧遞迴實現

八皇后--棧遞迴實現

說明

該文主要為練習資料結構的棧結構,所以將會一步步實現棧結構後,再使用棧的基本功能,如建棧,出棧,入棧,而不是直接呼叫庫函式

資料結構

  • 邏輯結構: 棧
  • 物理結構: 動態陣列實現

演算法

  • 遞迴思想

演算法描述

  1. 選擇棋盤第一行的任意一個點作為當前點p:(x,y)
  2. 結束條件:棧滿或是遍歷完所有點
  3. 判斷點p是否滿足攻擊條件:在同一行或同一列或同一斜線(正負45°),若滿足轉第4步,否則轉第5步
  4. 若y<8,當前點的y++,轉第2步;否則出棧,出棧點y++,作為當前點,轉第3步
  5. 當前點入棧,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; }