回溯算法
(1)基本概念
回溯算法實際上一個類似枚舉的搜索嘗試過程,主要是在搜索嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就“回溯”返回,嘗試別的路徑。
回溯法是一種選優搜索法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術爲回溯法,而滿足回溯條件的某個狀態的點稱爲“回溯點”。
許多複雜的,規模較大的問題都可以使用回溯法,有“通用解題方法”的美稱。
(2)基本思想
回溯算法的基本思想是:從一條路往前走,能進則進,不能進則退回來,換一條路再試。八皇后問題就是回溯算法的典型,第一步按照順序放一個皇后,然後第二步符合要求放第2個皇后,如果沒有位置符合要求,那麼就要改變第一個皇后的位置,重新放第2個皇后的位置,直到找到符合條件的位置就可以了。回溯在迷宮搜索中使用很常見,就是這條路走不通,然後返回前一個路口,繼續下一條路。回溯算法說白了就是窮舉法。不過回溯算法使用剪枝函數,剪去一些不可能到達 最終狀態(即答案狀態)的節點,從而減少狀態空間樹節點的生成。回溯法是一個既帶有系統性又帶有跳躍性的的搜索算法。它在包含問題的所有解的解空間樹中,按照深度優先的策略,從根結點出發搜索解空間樹。算法搜索至解空間樹的任一結點時,總是先判斷該結點是否肯定不包含問題的解。如果肯定不包含,則跳過對以該結點爲根的子樹的系統搜索,逐層向其祖先結點回溯。否則,進入該子樹,繼續按深度優先的策略進行搜索。回溯法在用來求問題的所有解時,要回溯到根,且根結點的所有子樹都已被搜索遍才結束。而回溯法在用來求問題的任一解時,只要搜索到問題的一個解就可以結束。這種以深度優先的方式系統地搜索問題的解的算法稱爲回溯法,它適用於解一些組合數較大的問題。
(3)解空間的樹結構
使用回溯法的解空間一般有兩種解空間:子集樹和排列樹
-
子集樹
當所給的問題從n個元素的集合S中找出滿足某種性質的子集時,相應的解空間樹稱爲子集樹。常用於0-1問題,如0-1揹包問題。
-
排列樹
當所給的問題是確定n個元素滿足某種性質的排列時,相應的解空間樹稱之爲排列樹。排序樹通常有n!葉結點。
(4)用回溯法解題的一般步驟
- 針對所給問題,確定問題的解空間:
首先明確定義問題的解空間,問題的解空間應該至少包含問題的一個解 - 確定結點擴展搜索規則
- 以深度優先方式搜索解空間,並在搜索過程中用剪枝函數避免無效搜索
確定瞭解空間的組織結構後,回溯法就從開始結點(根結點)出發,以深度優先的方式搜索整個解空間。這個開始結點就成爲一個活結點,同時也成爲當前的擴展結點。在當前的擴展結點處,搜索向縱深方向移至一個新結點。這個新結點就成爲一個新的活結點,併成爲當前擴展結點。如果在當前的擴展結點處不能再向縱深方向移動,則當前擴展結點就成爲死結點。此時,應往回移動(回溯)至最近的一個活結點處,並使這個活結點成爲當前的擴展結點。回溯法即以這種工作方式遞歸地在解空間中搜索,直至找到所要求的解或解空間中已沒有活結點時爲止。
(5)算法框架
-
問題框架
設問題的解是一個n維向量(a1,a2,………,an),約束條件是ai(i=1,2,3,……,n)之間滿足某種條件,記爲f(ai)。
-
遞歸回溯框架
回溯法是對解空間的深度優先搜索,在一般情況下使用遞歸函數來實現回溯法比較簡單,其中i爲搜索的深度,框架如下:
function backtrack (t) //t表示遞歸深度 { if (t>n) output(x); //n表示深度界限 else for (let i=f(n,t);i<=g(n,t);i++) // f(n,t),g(n,t)分別表示當前擴展結點未搜索過的子樹的起始編號和終止編號 { x[t]=h(i); if (constraint(t) && bound(t)) //滿足約束函數和限界函數 backtrack(t+1); } }
-
非遞歸的算法框架
function iterativeBacktrack () { let t=1; while (t>0) { if (f(n,t)<=g(n,t)) for (let i=f(n,t);i<=g(n,t);i++) { x[t]=h(i); if (constraint(t) && bound(t)) { if (solution(t)) output(x); else t++;} } else t--; } }
學習參考於: