演算法思想 回溯法

2021-09-25 21:25:27 字數 2339 閱讀 5147

很多經典的數學問題都可以用回溯演算法解決,比如數獨、八皇后、0-1 揹包、圖的著色、旅行商問題、全排列等等。

籠統地講,回溯演算法很多時候都應用在「搜尋」這類問題上。不過這裡說的搜尋,並不是狹義的指我們前面講過的圖的搜尋演算法,而是在一組可能的解中,搜尋滿足期望的解。

回溯演算法的理論知識很容易弄懂。不過,對於新手來說,比較難的是用遞迴來實現。所以,我們再通過兩個例子,來練習一下回溯演算法的應用和實現。

1.0-1 揹包

0-1 揹包是非常經典的演算法問題,很多場景都可以抽象成這個問題模型。這個問題的經典解法是動態規劃,不過還有一種簡單但沒有那麼高效的解法,那就是今天講的回溯演算法。動態規劃的解法我下一節再講,我們先來看下,如何用回溯法解決這個問題。

0-1 揹包問題有很多變體,我這裡介紹一種比較基礎的。我們有乙個揹包,揹包總的承載重量是 wkg。現在我們有 n 個物品,每個物品的重量不等,並且不可分割。我們現在期望選擇幾件物品,裝載到揹包中。在不超過揹包所能裝載重量的前提下,如何讓揹包中物品的總重量最大?

實際上,揹包問題我們在貪心演算法那一節,已經講過乙個了,不過那裡講的物品是可以分割的,我可以裝某個物品的一部分到揹包裡面。今天講的這個揹包問題,物品是不可分割的,要麼裝要麼不裝,所以叫 0-1 揹包問題。顯然,這個問題已經無法通過貪心演算法來解決了。我們現在來看看,用回溯演算法如何來解決。

對於每個物品來說,都有兩種選擇,裝進揹包或者不裝進揹包。對於 n 個物品來說,總的裝法就有 2^n 種,去掉總重量超過 wkg 的,從剩下的裝法中選擇總重量最接近 wkg 的。不過,我們如何才能不重複地窮舉出這 2^n 種裝法呢?

這裡就可以用回溯的方法。我們可以把物品依次排列,整個問題就分解為了 n 個階段,每個階段對應乙個物品怎麼選擇。先對第乙個物品進行處理,選擇裝進去或者不裝進去,然後再遞迴地處理剩下的物品。描述起來很費勁,我們直接看**,反而會更加清晰一些。

public int maxw = integer.min_value;

// 儲存揹包中物品總重量的最大值

// cw 表示當前已經裝進去的物品的重量和;i 表示考察到哪個物品了;

// w 揹包重量;items 表示每個物品的重量;n 表示物品個數

// 假設揹包可承受重量 100,物品個數 10,物品重量儲存在陣列 a 中,那可以這樣呼叫函式:

// f(0, 0, a, 10, 100)

public voidf(

int i,

int cw,

int[

] items,

int n,

int w)

f(i+

1, cw, items, n, w);if

(cw + items[i]

<= w)

}

2. 正規表示式

看懂了 0-1 揹包問題,我們再來看另外乙個例子,正規表示式匹配。

對於乙個開發工程師來說,正規表示式你應該不陌生吧?在平時的開發中,或多或少都應該用過。實際上,正規表示式裡最重要的一種演算法思想就是回溯。

正規表示式中,最重要的就是萬用字元,萬用字元結合在一起,可以表達非常豐富的語義。為了方便講解,我假設正表示式中只包含「」和「?」這兩種萬用字元,並且對這兩個萬用字元的語義稍微做些改變,其中,「」匹配任意多個(大於等於 0 個)任意字元,「?」匹配零個或者乙個任意字元。基於以上背景假設,我們看下,如何用回溯演算法,判斷乙個給定的文字,能否跟給定的正規表示式匹配?

我們依次考察正規表示式中的每個字元,當是非萬用字元時,我們就直接跟文字的字元進行匹配,如果相同,則繼續往下處理;如果不同,則回溯。

如果遇到特殊字元的時候,我們就有多種處理方式了,也就是所謂的岔路口,比如「*」有多種匹配方案,可以匹配任意個文字串中的字元,我們就先隨意的選擇一種匹配方案,然後繼續考察剩下的字元。如果中途發現無法繼續匹配下去了,我們就回到這個岔路口,重新選擇一種匹配方案,然後再繼續匹配剩下的字元。

有了前面的基礎,是不是這個問題就好懂多了呢?我把這個過程翻譯成了**,你可以結合著一塊看下,應該有助於你理解。

public class pattern 

public boolean match

(char

text,

int tlen)

private void

rmatch

(int ti,

int pj,

char

text,

int tlen)

if(pattern[pj]

=='*')}

else

if(pattern[pj]

=='?'

)else

if(ti < tlen && pattern[pj]

== text[ti])}

}

回溯法思想

回溯法是一種試探求解的方法,通過對問題的歸納分析,找出求解問題的乙個線索,沿著這一線索往前試 探,若試探成功,即得到解 若試探失敗,就逐步往回退,換其他路線再往前試探。因此,回溯法可以形象地概 況為 向前走,碰壁就回頭 回溯法從開始結點 根結點 出發,以深度優先的方式搜尋整個解空間 一般為樹結構空間...

回溯法思想

回溯法有 通用的解題法 之稱。可以用來系統地搜尋乙個問題的所有解或任一解。回溯法在問題的解空間 樹中,按照深度優先策略,從根結點出發搜尋解空間樹。演算法搜尋至解空間樹的任一結點時,先判斷該結點是否 包含問題的解。如果肯定不包含,則跳過對以該結點為根的子樹的搜尋,逐層向其祖先結點回溯。否則,進入該 子...

Leetcode刷題感想 回溯法

回溯法 回溯法往往用來找出乙個問題的所有可能解 而不是簡單的回答出有多少個解,如果是問解的個數,可能有更好的辦法,也就是動態規劃 回溯法是dfs的一種,但又和dfs不一樣,dfs往往求解連通面積,可達性的問題,而回溯法往往求解滿足某一條件的所有組合,比如二叉樹的路徑和,排列組合等經典問題。回溯法在 ...