Ⅰ 回溯法的基本思想是什麼
回溯演算法的基本思想是:從一條路往前走,能進則進,不能進則退回來,換一條路再試。 補充:
回溯法在問題的解空間樹中,按深度優先策略,從根結點出發搜索解空間樹。演算法搜索至解空間樹的任意一點時,先判斷該結點是否包含問題的解。如果肯定不包含,則跳過對該結點為根的子樹的搜索,逐層向其祖先結點回溯;否則,進入該子樹,繼續按深度優先策略搜索。
Ⅱ 程序員都應該精通的六種演算法,你會了嗎
對於一名優秀的程序員來說,面對一個項目的需求的時候,一定會在腦海里浮現出最適合解決這個問題的方法是什麼,選對了演算法,就會起到事半功倍的效果,反之,則可能會使程序運行效率低下,還容易出bug。因此,熟悉掌握常用的演算法,是對於一個優秀程序員最基本的要求。
那麼,常用的演算法都有哪些呢?一般來講,在我們日常工作中涉及到的演算法,通常分為以下幾個類型:分治、貪心、迭代、枚舉、回溯、動態規劃。下面我們來一一介紹這幾種演算法。
一、分治演算法
分治演算法,顧名思義,是將一個難以直接解決的大問題,分割成一些規模較小的相同問題,以便各個擊破,分而治之。
分治演算法一般分為三個部分:分解問題、解決問題、合並解。
分治演算法適用於那些問題的規模縮小到一定程度就可以解決、並且各子問題之間相互獨立,求出來的解可以合並為該問題的解的情況。
典型例子比如求解一個無序數組中的最大值,即可以採用分治演算法,示例如下:
def pidAndConquer(arr,leftIndex,rightIndex):
if(rightIndex==leftIndex+1 || rightIndex==leftIndex){
return Math.max(arr[leftIndex],arr[rightIndex]);
}
int mid=(leftIndex+rightIndex)/2;
int leftMax=pidAndConquer(arr,leftIndex,mid);
int rightMax=pidAndConquer(arr,mid,rightIndex);
return Math.max(leftMax,rightMax);
二、貪心演算法
貪心演算法是指在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。
貪心演算法的基本思路是把問題分成若干個子問題,然後對每個子問題求解,得到子問題的局部最優解,最後再把子問題的最優解合並成原問題的一個解。這里要注意一點就是貪心演算法得到的不一定是全局最優解。這一缺陷導致了貪心演算法的適用范圍較少,更大的用途在於平衡演算法效率和最終結果應用,類似於:反正就走這么多步,肯定給你一個值,至於是不是最優的,那我就管不了了。就好像去菜市場買幾樣菜,可以經過反復比價之後再買,或者是看到有賣的不管三七二十一先買了,總之最終結果是菜能買回來,但搞不好多花了幾塊錢。
典型例子比如部分背包問題:有n個物體,第i個物體的重量為Wi,價值為Vi,在總重量不超過C的情況下讓總價值盡量高。每一個物體可以只取走一部分,價值和重量按比例計算。
貪心策略就是,每次都先拿性價比高的,判斷不超過C。
三、迭代演算法
迭代法也稱輾轉法,是一種不斷用變數的舊值遞推新值的過程。迭代演算法是用計算機解決問題的一種基本方法,它利用計算機運算速度快、適合做重復性操作的特點,讓計算機對一組指令(或一定步驟)進行重復執行,在每次執行這組指令(或這些步驟)時,都從變數的原值推出它的一個新值。最終得到問題的結果。
迭代演算法適用於那些每步輸入參數變數一定,前值可以作為下一步輸入參數的問題。
典型例子比如說,用迭代演算法計算斐波那契數列。
四、枚舉演算法
枚舉演算法是我們在日常中使用到的最多的一個演算法,它的核心思想就是:枚舉所有的可能。枚舉法的本質就是從所有候選答案中去搜索正確地解。
枚舉演算法適用於候選答案數量一定的情況。
典型例子包括雞錢問題,有公雞5,母雞3,三小雞1,求m錢n雞的所有可能解。可以採用一個三重循環將所有情況枚舉出來。代碼如下:
五、回溯演算法
回溯演算法是一個類似枚舉的搜索嘗試過程,主要是在搜索嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就「回溯」返回,嘗試別的路徑。
許多復雜的,規模較大的問題都可以使用回溯法,有「通用解題方法」的美稱。
典型例子是8皇後演算法。在8 8格的國際象棋上擺放八個皇後,使其不能互相攻擊,即任意兩個皇後都不能處於同一行、同一列或同一斜線上,問一共有多少種擺法。
回溯法是求解皇後問題最經典的方法。演算法的思想在於如果一個皇後選定了位置,那麼下一個皇後的位置便被限制住了,下一個皇後需要一直找直到找到安全位置,如果沒有找到,那麼便要回溯到上一個皇後,那麼上一個皇後的位置就要改變,這樣一直遞歸直到所有的情況都被舉出。
六、動態規劃演算法
動態規劃過程是:每次決策依賴於當前狀態,又隨即引起狀態的轉移。一個決策序列就是在變化的狀態中產生出來的,所以,這種多階段最優化決策解決問題的過程就稱為動態規劃。
動態規劃演算法適用於當某階段狀態給定以後,在這階段以後的過程的發展不受這段以前各段狀態的影響,即無後效性的問題。
典型例子比如說背包問題,給定背包容量及物品重量和價值,要求背包裝的物品價值最大。
Ⅲ 數據結構中KMP演算法為什麼避免了不必要的回溯
因為它對匹配串也就是子串做了一個數組記錄每個字元的值,每次匹配不成功時就查看對應的值,然後移動母串指針,解決問題的關鍵就是對匹配串做了標記值。
Ⅳ 常見演算法思想6:回溯法
回溯法也叫試探法,試探的處事方式比較委婉,它先暫時放棄關於問題規模大小的限制,並將問題的候選解按某種順序逐一進行枚舉和檢驗。當發現當前候選解不可能是正確的解時,就選擇下一個候選解。如果當前候選解除了不滿足問題規模要求外能夠滿足所有其他要求時,則繼續擴大當前候選解的規模,並繼續試探。如果當前候選解滿足包括問題規模在內的所有要求時,該候選解就是問題的一個解。在試探演算法中,放棄當前候選解,並繼續尋找下一個候選解的過程稱為回溯。擴大當前候選解的規模,以繼續試探的過程稱為向前試探。
(1)針對所給問題,定義問題的解空間。
(2)確定易於搜索的解空間結構。
(3)以深度優先方式搜索解空間,並在搜索過程中用剪枝函數避免無效搜索。
回溯法為了求得問題的正確解,會先委婉地試探某一種可能的情況。在進行試探的過程中,一旦發現原來選擇的假設情況是不正確的,馬上會自覺地退回一步重新選擇,然後繼續向前試探,如此這般反復進行,直至得到解或證明無解時才死心。
下面是回溯的3個要素。
(1)解空間:表示要解決問題的范圍,不知道範圍的搜索是不可能找到結果的。
(2)約束條件:包括隱性的和顯性的,題目中的要求以及題目描述隱含的約束條件,是搜索有解的保證。
(3)狀態樹:是構造深搜過程的依據,整個搜索以此樹展開。
下面是影響演算法效率的因素:
回溯法搜索解空間時,通常採用兩種策略避免無效搜索,提高回溯的搜索效率:
為縮小規模,我們用顯示的國際象棋8*8的八皇後來分析。按照國際象棋的規則,皇後的攻擊方式是橫,豎和斜向。
皇後可以攻擊到同一列所有其它棋子,因此可推導出每1列只能存在1個皇後,即每個皇後分別占據一列。棋盤一共8列,剛好放置8個皇後。
為了擺放出滿足條件的8個皇後的布局,可以按如下方式逐步操作:
把規模放大到N行N列也一樣,下面用回溯法解決N皇後問題:
執行:
Ⅳ 五大基本演算法——回溯法
回溯法是一種選優搜索法(試探法)。
基本思想:將問題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-皇後問題
具體問題可網路詳細內容。
Ⅵ 六、遞歸與回溯演算法
在計算機領域裡面,很多問題都可以要採用遞歸演算法來解決。遞歸中,最長用到的方法就是回溯法。我們具體分析問題的時候,可以發現這類問題本質是一個樹的形狀。
遞歸演算法的本質還是將原來的問題轉化為了更小的同一問題,進行解決。一般注意兩點:
1、遞歸終止的條件。對應到了遞歸演算法中最基本的問題,也是最最簡單的問題。
2、遞歸過程。遞歸過程需要將原問題一步一步的推到更小的 同一 問題,更小的意思就是子問題解決起來就更加的簡單。有寫情況是能夠找到一個遞推的公式的。這個過程中就需要透徹的去理解遞歸函數的意義。明確這個函數的輸入和輸出是什麼,這樣來寫的話,就清晰多了。
因為有了這樣的遞歸公式,那麼我們就很容易的能看出來遞歸的終止條件就是我們知道的f(0)和f(1)了。有了f(0)和f(1)之後,我們就能夠繼續的遞推出f(3)一直到f(n)了。
但是我們現在才用一個遞歸演算法的思想來解決這個問題:
像我們常見的數據結構如鏈表、樹、圖等都是有著天然的遞歸結構的,鏈表由於是一個線性的結構,那麼通常我們也是能夠直接循環遍歷就能解決問題的,但是這里我們用遞歸法來解決一下LeetCode上面的問題
LeetCode 203 移除鏈表元素
分析:鏈表的結構可以理解成一個節點連接這一個更短的鏈表,這樣依次一直看下去,直到最後一個節點None,那麼我們這個時候的遞歸終止條件就是head指向None了,返回的就是None
深入的理解遞歸演算法之後,我們就開始進行回溯法的學習。通過LeetCode上面的幾道題,我們來深入的探討一下遞歸與回溯法的應用。
持續更新中...
數據結構與演算法系列博客:
一、數據結構與演算法概述
二、數組及LeetCode典型題目分析
三、鏈表(Linked list)以及LeetCode題
四、棧與隊列(Stack and Queue
五、樹(Trees)
六、遞歸與回溯演算法
七、動態規劃
八、排序與搜索
九、哈希表
參考資料
1、
2、
3、
Ⅶ 請問什麼是回溯演算法
回溯(backtracking)是一種系統地搜索問題解答的方法。為了實現回溯,首先需要為問題定義一個解空間(solution space),這個空間必須至少包含問題的一個解(可能是最優的)。
下一步是組織解空間以便它能被容易地搜索。典型的組織方法是圖(迷宮問題)或樹(N皇後問題)。
一旦定義了解空間的組織方法,這個空間即可按深度優先的方法從開始節點進行搜索。
回溯方法的步驟如下:
1) 定義一個解空間,它包含問題的解。
2) 用適於搜索的方式組織該空間。
3) 用深度優先法搜索該空間,利用限界函數避免移動到不可能產生解的子空間。
回溯演算法的一個有趣的特性是在搜索執行的同時產生解空間。在搜索期間的任何時刻,僅保留從開始節點到當前節點的路徑。因此,回溯演算法的空間需求為O(從開始節點起最長路徑的長度)。這個特性非常重要,因為解空間的大小通常是最長路徑長度的指數或階乘。所以如果要存儲全部解空間的話,再多的空間也不夠用。