《演算法》系列 大白話聊分治 回溯,手撕八皇后

2021-10-13 12:09:13 字數 3682 閱讀 8205

分治就是分而治之,即把乙個問題分解成很多個子問題,且這些問題與原問題結構相似,然後遞迴解決子問題,最後合併子問題的結果,得到原來問題的結果。

分治解題三個步驟:

分解:將問題分解成與原問題結構相似的子問題

解決:遞迴求解各個子問題,當子問題足夠小,直接返回結果(問題分解到足夠小,分解終止條件)

合併:將子問題的結果合併,得到原問題的結果

分治演算法也是一種遞迴,之前有寫到過遞迴的模板,這裡也貼一下分治演算法的模板。

接下來看個問題,深入了解一下分治演算法,並套用一下模板。

首先按照分治解題的三個步驟分析一下這個問題:

分解:x的n次冪可以分解為子問題:x^n/2 ……

解決:問題還沒有分解最小時,就進行遞迴,當問題分解到足夠小時,如n=0時,結果為1;n=1時,結果為x;n=-1時,結果為 1/x。

合併:將得到的結果合併

當n%2==0時,x^n = (x^n/2)*(x^n/2)

當n%2==1時,x^n = x* (x^n/2)*(x^n/2)

下面來看一下**:

public

double

mypow

(double x,

int n)

if(n ==1)

if(n ==-1

)//解決子問題,遞迴呼叫

double half =

mypow

(x, n /2)

;//這裡取模進行呼叫(考慮到n可能為負數)

double rest =

mypow

(x, n %2)

;//合併,返回結果

return rest * half * half;

}

這就是乙個典型分治演算法的題目,先對原問題進行分解,在求解子問題(遞迴求解,當問題足夠小的時候,返回結果),然後對子問題的結果進行合併。

回溯利用了試錯的思想,它嘗試分步的去解決問題。當分步計算的時候,發現答案錯誤或者無效時,它會取消上一步,甚至上幾步的操作,然後再通過其他的分步解答去找到正確答案。

來看看經典的八皇后問題

來說說解題思路,把這個題目放在這篇文章裡,當然就是用遞迴回溯法解決了…好了,接下來就該手撕八皇后了~

首先題目中要求每個皇后都不同行,不同列,也不在對角線上(其實就是題目給我們的過濾條件),不同行,不同列好理解,也好解決,我們就來看看不在對角線上(丿斜對角線、捺斜對角線)

丿對角線有個特點:行下標和列下標之和相等。(圖來自leetcode)

捺斜對角線有個特點:行下標和列下標之差相等。(圖來自leetcode)

好了,題目給我們的過濾條件就羅列出來了,我們需要去窮舉皇后出現的位置(不能被攻擊到),並記錄當前皇后位置可以攻擊到的位置。

很多分析都寫到**的注釋裡了,直接看**吧~

/**

* n皇后問題

* @param n n

* @return result

*/public list

>

solvenqueens

(int n)

/** * 遞迴函式,窮舉皇后的位置

* @param solutions 結果集

* @param queens 皇后可以放置的列(陣列記錄)

* @param n n個皇后

* @param row 當前遞迴是第 row 行

* @param columns 已有的皇后能攻擊到的列

* @param pie 已有的皇后能攻擊到的丿斜線

* @param na 已有的皇后能攻擊到的捺斜線

*/public

void

backtrack

(list

> solutions,

int[

] queens,

int n,

int row, set

columns, set

pie, set

na)else

//被攻擊的丿斜線包含了當前位置,跳過當前位置

if(pie.

contains

(row + i)

)//被攻擊的捺斜線包含了當前位置,跳過當前位置

if(na.

contains

(row - i)

)//記錄當前皇后存放的列

queens[row]

= i;

//記錄當前皇后攻擊到的列

columns.

add(i)

;//記錄當前皇后攻擊到的丿斜線

pie.

add(row+i)

;//記錄當前皇后攻擊到的捺斜線

na.add(row-i)

;//遞迴呼叫,窮舉下一行的皇后位置

backtrack

(solutions, queens, n, row +

1, columns, pie, na)

;/**

* 經過上面的遞迴,自頂向下遞迴完成時就已經窮舉出了一種皇后所有可能出現的位置

* 然後這裡需要把之前修改的資料進行清理,窮舉另一種皇后所有可能出現的位置,這裡是自底向上清理資料(即回溯)

*///清理皇后存放的列

queens[row]=-

1;//清理皇后攻擊的列

columns.

remove

(i);

//清理皇后攻擊的丿斜線

pie.

remove

(row+i)

;//清理皇后攻擊的捺斜線

na.remove

(row-i);}

}}/** *

* @param queens 皇后位置

* @param n n皇后

* @return 所有皇后的位置(用.和q表示)

*/public list

generateboard

(int

queens,

int n)

return board;

}

看完**你學廢了嗎?八皇后問題這裡主要是想來說明一下遞迴中的回溯,當我們列舉一種可能,到最後發現不正確時,需要清理之前的資料,即取消上一步,甚至上好多步的操作。遞迴函式呼叫(自頂向下)完成後去清理資料(自底向上),多理解一下這種歸去來兮的感覺。

分治:遞迴呼叫後,去合併遞迴得到的結果。

分治和回溯本質上都是遞迴的一種,具體的問題,我們要具體分析,是需要分解後合併結果,還是不斷窮舉試錯,然後回溯。

大白話系列 zookeeper

zookeeper主要服務於分布式系統,可以看做乙個分布式協調系統,主要是用來解決分布式應用中經常遇到的一些資料管理問題,如 統一命名服務 狀態同步服務 集群管理 分布式應用配置項的管理等。上面的解釋有點抽象,簡單來說zookeeper 檔案系統 監聽通知機制。zookeeper的資料結構,跟uni...

白話經典演算法系列

堆排序與快速排序,歸併排序一樣都是時間複雜度為o n logn 的幾種常見排序方法。學習堆排序前,先講解下什麼是資料結構中的二叉堆。二叉堆的定義二叉堆是完全二叉樹或者是近似完全二叉樹。二叉堆滿足二個特性 1 父結點的鍵值總是大於或等於 小於或等於 任何乙個子節點的鍵值。2 每個結點的左子樹和右子樹都...

大白話系列之C 委託與事件講解 一

從序言中,大家應該對委託和事件的重要性有點了解了吧,雖然說我們現在還是能模糊,但是從我的大白話系列中,我會把這些概念說的通俗易懂的。首先,我們還是先說說委託吧,從字面上理解,只要是中國人應該都知道這個意思,除非委託2個中文字不認識,舉個例子,小明委託小張去買車票。但是在我們的程式世界裡,也是這麼的簡...