帶有回溯演算法
❶ 回溯演算法的基本思想
回溯演算法也叫試探法,它是慎枯一種系統地搜索問題的解的方法。回溯演算法的基本思想是:從一條路往前走,能進則進,不能進則退回來,換一條路再試。
問題的解空間通常是在搜索問題的解的過程中動態產生的,這是回溯演算法的一個重要特性。
❷ 請問什麼是回溯演算法
回溯(backtracking)是一種系統地搜索問題解答的方法。為了實現回溯,首先需要為問題定義一個解空間(solution space),這個空間必須至少包含問題的一個解(可能是最優的)。
下一步是組織解空間以便它能被容易地搜索。典型的組織方法是圖(迷宮問題)或樹(N皇後問題)。
一旦定義了解空間的組織方法,這個空間即可按深度優先的方法從開始節點進行搜索。
回溯方法的步驟如下:
1) 定義一個解空間,它包含問題的解。
2) 用適於搜索的方式組織該空間。
3) 用深度優先法搜索該空間,利用限界函數避免移動到不可能產生解的子空間。
回溯演算法的一個有趣的特性是在搜索執行的同時產生解空間。在搜索期間的任何時刻,僅保留從開始節點到當前節點的路徑。因此,回溯演算法的空間需求為O(從開始節點起最長路徑的長度)。這個特性非常重要,因為解空間的大小通常是最長路徑長度的指數或階乘。所以如果要存儲全部解空間的話,再多的空間也不夠用。
❸ 常見演算法思想6:回溯法
回溯法也叫試探法,試探的處事方式比較委婉,它先暫時放棄關於問題規模大小的限制,並將問題的候選解按某種順序逐一進行枚舉和檢驗。當發現當前候選解不可能是正確的解時,就選擇下一個候選解。如果當前候選解除了不滿足問題規模要求外能夠滿足所有其他要求時,則繼續擴大當前候選解的規模,並繼續試探。如果當前候選解滿足包括問題規模在內的所有要求時,該候選解就是問題的一個解。在試探演算法中,放棄當前候選解,並繼續尋找下一個候選解的過程稱為回溯。擴大當前候選解的規模,以繼續試探的過程稱為向前試探。
(1)針對所給問題,定義問題的解空間。
(2)確定易於搜索的解空間結構。
(3)以深度優先方式搜索解空間,並在搜索過程中用剪枝函數避免無效搜索。
回溯法為了求得問題的正確解,會先委婉地試探某一種可能的情況。在進行試探的過程中,一旦發現原來選擇的假設情況是不正確的,馬上會自覺地退回一步重新選擇,然後繼續向前試探,如此這般反復進行,直至得到解或證明無解時才死心。
下面是回溯的3個要素。
(1)解空間:表示要解決問題的范圍,不知道範圍的搜索是不可能找到結果的。
(2)約束條件:包括隱性的和顯性的,題目中的要求以及題目描述隱含的約束條件,是搜索有解的保證。
(3)狀態樹:是構造深搜過程的依據,整個搜索以此樹展開。
下面是影響演算法效率的因素:
回溯法搜索解空間時,通常採用兩種策略避免無效搜索,提高回溯的搜索效率:
為縮小規模,我們用顯示的國際象棋8*8的八皇後來分析。按照國際象棋的規則,皇後的攻擊方式是橫,豎和斜向。
皇後可以攻擊到同一列所有其它棋子,因此可推導出每1列只能存在1個皇後,即每個皇後分別占據一列。棋盤一共8列,剛好放置8個皇後。
為了擺放出滿足條件的8個皇後的布局,可以按如下方式逐步操作:
把規模放大到N行N列也一樣,下面用回溯法解決N皇後問題:
執行:
❹ 回溯演算法(C/C++)
回溯法是一種選優搜索法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,而滿足回溯條件的某個狀態的點稱為「回溯點」。許多復雜的,規模較大的問題都可以使用回溯法。
回溯演算法類似於枚舉的過程,當搜索時遇到不滿足條件,回退到上一個,嘗試別的路徑。
回溯是遞歸的產物,有遞歸一定有回溯。
回溯演算法並不是什麼高效的演算法,因為本質上時去遍歷所有元素,找出所有可能,然後選出需要的答案。那為什麼還要回溯法,簡單來說,不是所有的問題都能用什麼巧妙的方法來解決的,有些問題你能暴力求解出來就不錯了。
這里是綜合了一下參考的別人寫的,有這么幾種情況適合回溯法解決:
回溯法使用多了不難發現,回溯法的問題都可以抽象轉換為樹型結構,你可以畫一棵樹來分析這類問題,因為回溯法解決的都是在集合中遞歸查找子集,集合的大小就構成了樹的寬度,遞歸的深度,都構成的樹的深度。因為遞歸就要有終止條件,所以必然是一顆高度有限的樹(N叉樹)。
for循環可以理解是橫向遍歷,backtracking(遞歸)就是縱向遍歷
有效的 IP 地址 正好由四個整數(每個整數位於 0 到 255 之間組成,且不能含有前導 0),整數之間用 '.' 分隔。例如:"0.1.2.201" 和 "192.168.1.1" 是 有效的 IP 地址,但是"0.011.255.245"、"192.168.1.312" 和 "[email protected]" 是 無效的 IP 地址。
問題想到用for可以解,但是這要多少層for啊,我們試一下回溯的方法。
1.首先確定參數
全局變數數組path存放切割後迴文的子串,二維數組result存放結果集。本題遞歸函數參數還需要startIndex,因為切割過的地方,不能重復切割,和組合問題也是保持一致的。如果是一個集合來求組合的話,就需要startIndex,startIndex來控制for循環的起始位置。
2.遞歸終止條件
本題明確要求只會分成4段,所以用分割的段數作為終止條件。pointNum表示點的數量,pointNum為3說明字元串分成了4段了。然後驗證一下第四段是否合法,如果合法就加入到結果集里。
3.單層搜索的邏輯
在for (int i = startIndex; i < s.size(); i++)循環中 [startIndex, i]這個區間就是截取的子串,需要判斷這個子串是否合法。
如果合法就在字元串後面加上符號.表示已經分割。
如果不合法就結束本層循環,剪掉此分支。
然後就是遞歸和回溯的過程,遞歸調用時,下一層遞歸的startIndex要從i+2開始(因為需要在字元串中加入了分隔符.),同時記錄分割符的數量pointNum 要 +1。回溯的時候,就將剛剛加入的分隔符. 刪掉就可以了,pointNum也要-1。
文章參考: https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw
https://mp.weixin.qq.com/s/v--VmA8tp9vs4bXCqHhBuA
https://www.jianshu.com/p/4abfd96d91e6
❺ 五大基本演算法——回溯法
回溯法是一種選優搜索法(試探法)。
基本思想:將問題P的狀態空間E表示成一棵高為n的帶全有序樹T,把求解問題簡化為搜索樹T。搜索過程採用 深度優先搜索 。搜索到某一結點時判斷該結點是否包含原問題的解,如果包含則繼續往下搜索,如果不包含則向祖先回溯。
通俗來說,就是利用一個樹結構來表示解空間,然後從樹的根開始深度優先遍歷該樹,到不滿足要求的葉子結點時向上回溯繼續遍歷。
幾個結點:
擴展結點:一個正在產生子結點的結點稱為擴展結點
活結點:一個自身已生成但未全部生成子結點的結點
死結點:一個所有子結點已全部生成的結點
1、分析問題,定義問題解空間。
2、根據解空間,確定解空間結構,得 搜索樹 。
3、從根節點開始深度優先搜索解空間(利用 剪枝 避免無效搜索)。
4、遞歸搜索,直到找到所要求的的解。
1、子集樹
當問題是:從n個元素的集合S中找出滿足某種性質的子集時,用子集樹。
子集樹必然是一個二叉樹。常見問題:0/1背包問題、裝載問題。
遍歷子集樹時間復雜度:O(2^n)
2、排列樹
當問題是:確定n個元素滿足某種排列時,用排列數。常見問題:TSP旅行商問題,N皇後問題。
遍歷排列樹時間復雜度:O(n!)
通俗地講,結合Java集合的概念,選擇哪種樹其實就是看最後所得結果是放入一個List(有序)里,還是放入一個Set(無序)里。
剪枝函數能極大提高搜索效率,遍歷解空間樹時,對於不滿足條件的分支進行剪枝,因為這些分支一定不會在最後所求解中。
常見剪枝函數:
約束函數(對解加入約束條件)、限界函數(對解進行上界或下界的限定)
滿足約束函數的解才是可行解。
1、0/1背包問題
2、TSP旅行商問題
3、最優裝載問題
4、N-皇後問題
具體問題可網路詳細內容。
❻ 回溯演算法與貪心演算法
回溯是遞歸的副產品,只要有遞歸就會有回溯 ,所以回溯法也經常和二叉樹遍歷,深度優先搜索混在一起,因為這兩種方式都是用了遞歸。
回溯法就是暴力搜索,並不是什麼高效的演算法,最多再剪枝一下。
回溯演算法能解決如下問題:
組合問題:N個數裡面按一定規則找出k個數的集合
排列問題:N個數按一定規則全排列,有幾種排列方式
切割問題:一個字元串按一定規則有幾種切割方式
子集問題:一個N個數的集合里有多少符合條件的子集
棋盤問題:N皇後,解數獨等等
回溯演算法的本質是縱向遍歷
回溯演算法模板為
貪心的本質是選擇每一階段的局部最優,從而達到全局最優
貪心演算法一般分為如下四步:
將問題分解為若干個子問題
找出適合的貪心策略
求解每一個子問題的最優解
將局部最優解堆疊成全局最優解
eg:擺動序列
如果連續數字之間的差嚴格地在正數和負數之間交替,則數字序列稱為擺動序列。第一個差(如果存在的話)可能是正數或負數。少於兩個元素的序列也是擺動序列。
例如, [1,7,4,9,2,5] 是一個擺動序列,因為差值 (6,-3,5,-7,3) 是正負交替出現的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是擺動序列,第一個序列是因為它的前兩個差值都是正數,第二個序列是因為它的最後一個差值為零。
給定一個整數序列,返回作為擺動序列的最長子序列的長度。 通過從原始序列中刪除一些(也可以不刪除)元素來獲得子序列,剩下的元素保持其原始順序。
示例 2:
輸入: [1,17,5,10,13,15,10,5,16,8]
輸出: 7
解釋: 這個序列包含幾個長度為 7 擺動序列,其中一個可為[1,17,10,13,10,16,8]。