回溯法也叫試探法,它是一種系統地搜索問題之解的方法。「回溯」,這個詞自己有逆流而上的意思。而回溯的過程正是當某一種可能的試探結果否認了該可能路徑的正確性後返回先前的某個狀態繼續進行其餘可能性的試探的過程。能夠說回溯策略並不是按照某種固定的計算方法來設計算法,而是經過嘗試和糾正錯誤來尋找答案。下面將使用回溯思想解決若干經典問題並經過它們來講明使用回溯的基本思路 什麼叫回溯法 回溯是一種比較簡單、比較經常使用的搜索策略。 它的基本思想是假設某問題的解決步驟可能有N步,且每一步的解決方法又可能有M種,那麼就按照某種順序依次試探每一步中的各類方法,一旦某一步的全部方法都失效,那麼就返回上一步繼續試探上一步驟的其餘M−1種方法。簡而言之就是從一條路往前走,能進則進,不能進則退回來,換一條路再試。 一般用回溯法解決問題的通常步驟爲:首先,定義一個解空間,它包含問題的解,也就是每一步所採用的各類方法。而後利用適於搜索的方法組織解空間,再利用深度優先法搜索解空間。並利用限界函數避免移動到不可能產生解的子空間。這裏問題的解空間一般是在搜索問題的解的過程當中動態產生的,這是回溯法的一個重要特性。而其中全面訪問全部可能的狀況又能夠分爲兩種:一是不考慮給定問題的特有性質,按事先定好的順序,依次運用規則,即盲目搜索的方法;另外一種則考慮問題給定的特有性質,選用合適的規則,提升搜索的效率,即啓發式的搜索。 下面簡單舉幾個例子來闡釋回溯法 ---- 迷 宮 問 題 ---- 迷宮問題是應用回溯法解決的典型問題。迷宮早出如今古希臘神話中。傳說在遠古時代,麥諾安帝國國王彌諾斯在克里特島建造了一座有無數宮殿的迷宮,迷宮中道路曲折縱橫,誰進去都別想出來,並且在迷宮的縱深處還有一個牛頭怪。彌諾斯要求雅典每隔 9 年送 7 對童男童女到克里特島。這些童男童女就是供奉給牛頭怪吃的。 來自雅典的王子忒修斯立志要爲民除害,因而他與當年選獻的童男童女一塊兒來到克里特島,忒修斯上岸後愛上了美麗的阿里阿德涅公主。當公主知道忒修斯的使命後,就送給他一把魔劍和一個線球,以避免忒修斯受到牛頭怪的傷害。 勇敢的忒修斯一進入迷宮,就將線球的一端拴在迷宮的入口處,而後放開線團,沿着曲折複雜的通道,向迷宮深處走去。後,他終於找到了牛頭怪,並用阿里阿德涅公主給的劍,奮力殺死了牛頭怪。而後,他帶着童男童女,順着線路走出了迷宮。在阿里阿德涅公主的幫助下,忒修斯等人一塊兒逃出了克里特島。 現假設迷宮由一系列交叉路口組成,從一個入口進入後能夠沿着 3 個方向走:向左、向前或者向右。迷宮的路徑由一系列標識路口的序號組成。爲了求解迷宮問題,須要用到回溯的方法,當沿着某一路徑一步步走向出口卻發現進入死衚衕之時,就回溯一步或多步,尋找其餘可走的路徑。 以下圖所示爲一個迷宮。從入口 1 的位置出發,沿東走到節點 2 時發現有兩條路。標記節點 2,而後繼續向東走,直到走到盡頭無路可走時回溯到節點 2 嘗試另一種可能的走法,即向右轉,沿南走到節點4,這時有3個選擇,能夠向右、向前或者向左。結果發現向左或向右都走不通,則再次退回到節點 4 向前,沿南走到節點 5。如此繼續下去,則能夠終到達出口 10 的位置。下面給出了求解迷宮問題的示例程序。 迷宮面試
由此例能夠簡單總結出應用回溯法的通常思路。即首先必須明肯定義問題的解空間。而問題的解空間應當至少包含該問題的一個解。對於迷宮問題,各路口的集合就是問題的解空間。對於每一個節點遍歷嘗試的可能性多不超過 3 種,即左轉、右轉和向前,僅當這 3 種可能性都被否認時纔回溯至前一狀態。在定義了問題的解空間以後還應當考慮如何將解空間進行有效的組織,以使得回溯法可以方便地搜索這些子空間中的節點。在必要的時候還應當注意優化搜索的策略以提升算法的實時性。 當以上準備完成以後就從出發點開始,以深度優先的方式對整個解空間進行搜索。該出發點隨即被更新爲當前的擴展節點。也就是從一個可能的路徑進行深刻以產生下一個新的節點,並將新的節點更新爲擴展節點。一旦當前的擴展節點既不能得出整個問題的一個解,也不能再繼續向更深的方向進行搜索,那麼就返回上一個節點並將上一個節點從新更新爲新的擴展節點,再嘗試另外一種可能性。回溯法正是採用這種工做方式以遞歸爲基礎在解空間內開展系統的搜索工做,直到求出問題的解或者代表問題無解爲止。須要說明的是由於回溯法是對解空間的深度優先搜索,因此能夠考慮使用樹結構的遞歸遍歷方式完成搜索工做。固然這並不是是惟一的途徑,也能夠考慮使用樹結構的非遞歸遍歷方法,那樣整個回溯過程將以迭代的形式完成。 ---- 八皇后問題 ---- 八皇后問題是一個古老而著名的問題,它是回溯法的典型例題。該問題早是由德國棋手馬克斯•貝瑟爾(Max Bezzel)於 1848 年提出。以後陸續有數學家對其進行研究,其中包括德國數學家卡爾•弗里德里希•高斯(Karl Friedrich Gauss)和格奧爾格•康託(Georg Cantor),該文是這樣描述的:在 8 行 8 列的國際象棋棋盤上擺放着 8 個皇后。若 2 個皇后位於同一行、同一列或同一對角線上,則稱它們爲互相攻擊。在國際象棋中皇后是強大的棋子,由於它的攻擊範圍大,下圖顯示了一個皇后的攻擊範圍。算法
如今要求使這 8 個皇后不能相互攻擊,即任意 2 個皇后都不能處於同一行、同一列或同一對角線上,問有多少種擺法。高斯認爲有 76 種方案。1854 年在柏林的象棋雜誌上不一樣的做者發表了 40 種不一樣的解,後來有人用圖論的方法解出 92 種結果。現代教學中,把八皇后問題當成一個經典遞歸算法例題。下圖顯示了兩種 8 個皇后不相互攻擊的狀況。編程
如今來看如何使用回溯法解決八皇后問題。這個算法將在棋盤上一列一列地擺放皇后直到 8 個皇后在不相互攻擊的狀況下都被擺放在棋盤上,算法便終止。當一個新加入的皇后由於與已經存在的皇后之間相互攻擊而不能被擺在棋盤上時,算法便發生回溯。一旦發生這種狀況,就試圖把後放在棋盤上的皇后移動到其餘地方。這樣作是爲了讓新加入的皇后可以在不與其餘皇后相互攻擊的狀況下被擺放在棋盤的適當位置上。 如圖下圖的狀況(須要發生回溯的狀況),儘管第 7 個皇后不會與已經放在棋盤上的任何一個皇后發生攻擊,但仍然須要將它移除併發生回溯,由於沒法爲第 8 個皇后在棋盤上找到合適的位置。 算法的回溯部分將嘗試移動第 7 個皇后到第 7 列的另一點來爲第 8 個皇后在第 8 列尋找一個合適的位置。若是第7個皇后因爲在第7列找不到合適的位置而沒法被移動,那麼算法就必須去掉它並回溯到第 6 列的皇后。終算法不斷重複着擺放皇后和回溯的過程直到找到問題的解爲止。數據結構
因爲回溯法也是在試圖搜索整個解空間中的全部可能的選擇,因而有人會誤認爲回溯法與窮舉法差很少,但事實上回溯法要較窮舉法在效率上高出不少。這裏就以一種簡單的估算方法來對八皇后問題進行一下分析。首先採用窮舉法,那麼能夠容易得出該問題解空間的節點總數爲 109601。而後在使用回溯法的前提下隨機選取 20 條路徑,分別估計它們的節點個數並求得總數的平均值。由於已知八皇后問題共有 92 種解,因此選擇 20 種隨機路徑進行估計所得出的結果已經能夠較爲接近實際數值。通過計算得出回溯法產生的節點數的平均值約爲 1740,這相對於 109601 不足 2%。可見回溯法做爲一種跳躍性和系統性相結合的搜索方法是具備較高效率的。 下面給出了求解八皇后問題的示例程序。併發
相關圖書函數
《算法之美:隱匿在數據 結構背後的原理(C++版)》 45個經常使用算法、22個經典問題 點撥面試熱點,生活化實例詳解 通俗易懂,系統全面 左飛 著
2016年3月出版優化
√ 探祕算法世界、求索數據結構之道 √ 聚集經典問題、暢享編程技法之趣 √ 點撥求職熱點、敲開業界名企之門spa