圖說演算法使用JavaScript(十三)

6-4八皇后演算法

         八皇后問題也是一種常見的堆疊應用實例。在西洋棋中的皇后可以在沒有限定一步走幾格的前提下,對其盤中的其他棋子直吃、橫吃及對角斜吃(左斜吃或右斜吃皆可),只要後放入的新皇后,再放入前必須考慮所放置直線方向、橫線方向或對角線方向是否已被放置就皇后,否則就會被先放入的舊皇后吃掉。
        利用這個觀念,我們可以將其應用在4*4的棋盤,就稱為4-皇后問題;應用在8*8的棋盤,就稱為8-皇后問題。應用在N*N的棋盤,就稱為N-皇后問題。要解決N-皇后問題(在此我們以8-皇后為例),首先當於棋盤中置入一新皇后,且這個位置不被先前放置的皇后吃掉,則將此新皇后的位置存入堆疊。
        但若欲放置新皇后的該行(或該列)的8個位置,都沒有辦法放置新皇后(亦即一放入任何一個位置,就會被先前放置的舊皇后給吃掉)。此時,就必須由堆疊中取出前一個皇后的位置,並於該行(或該列)中重新尋找另一個新的位置放置,在將該位置存入堆疊中,而這種方式就是一種回溯Backtracking)演算法的應用概念。
          N-皇后問題的解答,就是配合堆疊及回溯兩種演算概念,以逐行(或逐列)找新皇后位置(如果找不到,則回溯到前一行找尋前一個皇后另一個新位置,以此類推)的方式,來尋找N-皇后問題的其中一組解答。

JS                queen.js

程式碼:如下

const EIGHT=8;  //定義最大堆疊容量
queen = [];  //存放8個皇后之列位
number=0;   //計算總共幾組解的總數
//決定皇后存放的位置
//輸出所需要的結果

var print_table=()=> {
	let x=y=0;
	number+=1;
	process.stdout.write('\n');
	process.stdout.write('八皇后問題的第'+number+'組解\n\t');
	for (x=0; x<EIGHT ; x++) {
		for (y=0; y<EIGHT ; y++){
			if (x==queen[y])
				process.stdout.write('<q>');
			else 
				process.stdout.write('<->');
		}
		process.stdout.write('\n\t');
	}
}

//測試在(row,col)上的皇后是否遭受攻擊
//若遭受攻擊則傳回值為1,否則傳回0
var attack=(row, col)=>{
	let i=0;
	atk=false;
	offset_row=offset_col=0;
	while ((atk!=1) && i<col) {
		offset_col=Math.abs(i-col);
		offset_row=Math.abs(queen[i]-row);
		//判斷兩皇后是否在同一對角線上
		if ((queen[i]==row || offset_row==offset_col)) 
			atk=true;
		i=i+1;
	}
	return atk;
}

var decide_position=(value)=>{
	let i=0;
	while (i<8) {
		//是否受到攻擊攻擊判斷式
		if (attack(i,value)!=1) {
			queen[value]=i;
			if (value==7)
				print_table();
			else
				decide_position(value+1);
		}
		i++;
	}
}

//主程式
decide_position(0);

6-5 陣列實作佇列

         以陣列結構來製作佇列的好處是演算法相當簡單,不過與堆疊不同之處是需要擁有兩種基本動作加入與刪除,而且使用frint與rear兩個註標來分別指向佇列的前端與尾端,缺點是陣列大小並無法事先規劃宣告。首先我們需要宣告一個有限容量的陣列,並以下列說明:

const MAXSIZE=4;
queue=[];       //佇列大小為4
front=-1;
rear=-1;

const MAX=10;      //定義佇列的大小
queue=[];
var front=rear=-1;
var choice='';
const prompt = require ('prompt-sync')();
while (rear<MAX-1 && choice !='e') {
	const choice = prompt('[a]表示存入一個數值[d]表示取出一個數值[e]表示跳出此程式: ');
	if (choice=='a') {
		const val = parseInt(prompt('[請輸入數值]: '));
		rear+=1;
		queue[rear]=val;
	}
	else if (choice=='d') {
		if (rear>front) {
			front+=1;
			process.stdout.write('[取出數值為]: ' +queue[front]+'\n');
			queue[front]=0;
		}
		else{
			process.stdout.write('[佇列已經空了]\n');
			return;
		}
	}
	else {
		process.stdout.write('\n');
		break;
	}
}
process.stdout.write('---------------------------\n');
process.stdout.write('[輸出佇列中所有元素]: \n');

if (rear==MAX-1)
	process.stdout.write('[佇列已滿]\n');
else if (front>=rear)  {
	process.stdout.write('沒有\n');
	process.stdout.write('[佇列已空]\n');
}
else  {
	while (rear>front) {
		front+=1;
		process.stdout.write('['+queue[front]+'] ');
	}
	process.stdout.write('\n');
	process.stdout.write('---------------------------------------------\n');
}
process.stdout.write('\n');

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料