算法學習筆記:回溯


回溯

  • 從問題的某一種可能出發, 搜索從這種情況出發所能達到的所有可能, 當這一條路走到” 盡頭 “的時候, 再倒回出發點, 從另一個可能出發, 繼續搜索。這種不斷” 回溯 “尋找解的方法, 稱作” 回溯法 “。
  • 回溯是一種算法思想,可以用遞歸實現。
  • 通俗點講回溯就是一種試探,類似於窮舉,但回溯有“剪枝”功能,比如求和問題。給定7個數字,1 2 3 4 5 6 7求和等於7的組合,從小到大搜索,選擇1+2+3+4 =10>7,已經超過了7,之後的5 6 7就沒必要在繼續了,這就是一種搜索過程的優化。

八皇后問題

  • 如何能夠在 8×8 的國際象棋棋盤上放置八個皇后,使得任何一個皇后都無法直接吃掉其他的皇后?
  • 爲了達到此目的,任兩個皇后都不能處於同一條橫行、縱行或斜線上。

算法

  1. 逐一嘗試當前皇后的擺放位置
  2. 如果當前皇后與前面任意皇后處於同一條橫行、縱行或斜線上,則跳過循環。
  3. 如果當前皇后沒有衝突,則確定擺放位置,並擺放下一位皇后
  4. 如果所有皇后擺放完畢,則算法結束。

代碼

class EightQueen {
	
	int N;
	
	int[] pos;
	
	int[] result;
	
	public EightQueen() {
		
		this.N = 8;
		this.pos = new int[this.N];
		this.result = new int[this.N];
	}
	
	public void setQueen(int k) {
		
		//函數出口
		if (k == this.N) {
			//保存結果,因爲有多種解,可以將結果保存到鏈表裏面。
			//本文爲了簡便,只保存了一組結果。
			for (int i = 0; i < pos.length; i++) {
				this.result[i] = this.pos[i];
			}
			return;
		}
		
		for (int j = 0; j < pos.length; j++) {
			int i;
			for (i = 0; i < k; i++) {
				//列重複
				//斜線重複,|x|=|y|的幾何圖形正好是斜線
				if (this.pos[i] == j || Math.abs(j - this.pos[i]) == Math.abs(i - k)) {
					break;
				}
			}
			if (i == k) {
				this.pos[i] = j;
				setQueen(k + 1);
			}
		}
	}
	
	public void showResult() {
		
		for (int i = 0; i < result.length; i++) {
			for (int j = 0; j < result.length; j++) {
				System.out.print(this.result[i] == j ? "X " : "O ");
			}
			System.out.println();
		}
	}
	
	public static void main(String[] args) {
		
		EightQueen eQueen = new EightQueen();
		eQueen.setQueen(0);
		eQueen.showResult();
	}
}

核心回溯函數

	public void setQueen(int k) {
		
		if (k == this.N) {
			for (int i = 0; i < pos.length; i++) {
				this.result[i] = this.pos[i];
			}
			return;
		}
		
		for (int j = 0; j < pos.length; j++) {
			int i;
			for (i = 0; i < k; i++) {
				//列重複
				//斜線重複,|x|=|y|的幾何圖形正好是斜線
				if (this.pos[i] == j || Math.abs(j - this.pos[i]) == Math.abs(i - k)) {
					break;
				}
			}
			if (i == k) {
				this.pos[i] = j;
				setQueen(k + 1);
			}
		}
	}

重點理解

  1. 皇后在棋盤上的位置本來應該用二維矩陣表示,但是因爲每行只有一個皇后,所以可以用一維數組簡化代替。
  2. k既可以理解爲皇后的序號,也可以理解爲矩陣的row。
  3. 通常我們對數據(比如矩陣)做雙重循環時,習慣性的將外部for循環用於row的迭代,內部for循環用於col的迭代。因爲內部循環速度快於外部,於是我們就可以對數據進行從上到下,從左到右的操作。但在這個回溯函數中,我們的外部循環反相當於col迭代,這是因爲,對於每一個可能的位置,我們都要和前面所有已經就爲的皇后進行比對。
  4. pos[i] == j表示當我們將皇后k擺放在j位置時,很不幸皇后i也在j位置,所以列重複
  5. Math.abs(k - i) == Math.abs(j - pos[i])表示斜線重複,斜線有4個方向,45°角左上右上左下右下,剛好就是幾何方程x=y|x|=|y|的樣子,進一步展開就是x1x2=y1y2|x1-x2|=|y1-y2|

結果

O O O O O O O X 
O O O X O O O O 
X O O O O O O O 
O O X O O O O O 
O O O O O X O O 
O X O O O O O O 
O O O O O O X O 
O O O O X O O O 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章