【算法】回溯


回溯

1.概念

回溯是很經典的一個算法,什么是回溯,回溯其實是一種暴力枚舉的方式,為啥都暴力了還是很經典的一種方法呢,其實是因為有些問題我們能暴力出來就不錯了,就別要其他自行車了。常見的回溯類問題:組合;排列;切割;子集;棋牌;

其實回溯算法就是常說的DFS,本質上是一種暴力枚舉算法;

回溯算法常用於解決的問題:
組合
排列
切割
子集
棋盤:N皇后

2.過程

這是回溯的第一道題,回溯是很經典的一個算法,什么是回溯,回溯其實是一種暴力枚舉的方式,為啥都暴力了還是很經典的一種方法呢,其實是因為有些問題我們能暴力出來就不錯了,就別要其他自行車了。常見的回溯類問題:組合;排列;切割;子集;棋牌;

比如最經典的排列。從1,2,3,4,5中取3個數組成排列有多少種,我們肯定會解決這種問題,但是程序怎么寫呢。想一下我們解決這個問題的過程,我們先選1,然后第二個數可以選2,第三個數可以選3,這是一種答案了,然后呢,換第三個數,第三個數選4,又一種答案,再換,第三個數選5,沒得選了,所以以12打頭的數都選完了,得到三種答案.然后再換第二個數,第二個數選3,然后第三個數選4,注意是組合問題所以我們不能退往回選2了,不然就重復了。就是這樣一種選擇方案,一直到第3個數選成了3,得到答案345,就不用往后進行了。

你看,這其實就是一個多叉樹啊!每走一步我們都要做出自己的選擇,然后在該選擇的基礎上做下一步選擇,直到這個選擇達到了題目要求,然后我們放棄我們上一步做的選擇,去換另外一種選擇試一試。這個換掉我們上一步做的選擇就是回溯的過程,也就是“撤銷選擇”。因為只有把上一步的選擇撤銷了我們才能夠得到新的選擇,比如123,只有把3撤銷了我們才能去選擇4.

這個過程中有遞歸嗎?當然有啊,我們把問題縮小一點,比如123三個數字的全排列,首先1打頭,得到123,132,這其實就是1+[2,3]的全排列;遞歸體現在這里!

回溯和遞歸相輔相成,前面也說過了這就是一顆樹,而樹就一定會用到遞歸。這棵樹我們起了一個名字叫做決策樹,每走一步都是在做一次選擇一次決策,就和我們的人生一樣。想象一下回溯、深度優先搜索(DFS),遞歸,都有一種“不撞南牆不死心" 的意思,而這個南牆就是我們的結束條件。 。

3.模板

解決一個回溯問題,實際上就是一個決策樹的遍歷過程
主要需要思考三個問題:

  • 路徑:記錄我們做出了的選擇(走過的決策樹上的路徑,我們一般都是在最后的葉子節點上去收集結果);【比如我們選的123,124】;
  • 選擇列表:當前情況下我們可以做出的選擇;【比如在第三步我們可以選3.4.5】
  • 結束條件:也就是到達了決策樹的底層葉子節點,選擇列表為空了,無法再做出別的選擇了。【比如我們的樹選完了123達到題目中的要求3個元素了,就不能夠再做選擇了】

回溯算法的模板:

result = []   //結果集
def backtrack(路徑, 選擇列表):
    if 滿足結束條件:
        result.add(路徑)  //把已經做出的選擇添加到結果集;
        return  //一般的回溯函數返回值都是空;

    for 選擇 in 選擇列表: //其實每個題的不同很大程度上體現在選擇列表上,要注意這個列表的更新,
    //比如可能是搜索起點和終點,比如可能是已經達到某個條件,比如可能已經選過了不能再選;
        做選擇  //把新的選擇添加到路徑里;路徑.add(選擇)
        backtrack(路徑, 選擇列表) //遞歸;
        撤銷選擇  //回溯的過程;路徑.remove(選擇)

核心就是for循環里的遞歸,在遞歸之前做選擇,在遞歸之后撤銷選擇;

在這個過程中還有一點很重要,就是我們其實是在做兩種遍歷;

  • 橫向遍歷(for):其實就是我們在不停的做着的選擇;
  • 縱向遍歷(遞歸):其實就是在做完選擇后面臨的下一輪選擇;

其實不同的情境下最大的不同就在於決策列表的更新,比如說搜索起點和終點,比如說是否已經被選過,比如說是否達到某個條件(只要求k個數或者和為目標值);;

4.樣例

比如說下面的從4個數中選2個樹組合,這就是對應的決策樹。

image

其實每一個節點都是在做着同樣的事情,只不過選擇列表不一樣了,而這也是每道里最大的不一樣。

再換個角度去看這決策樹:

  • 橫向遍歷(for):從左到右做決策
  • 縱向遍歷(遞歸):在做完決策后開始下一輪決策

image

這個樣例就是我們的下面這道題目

77. 組合

39. 組合總和

40. 組合總和 II

216. 組合總和 III

46. 全排列

47. 全排列 II

51. N 皇后

5.體會

  • 1.只要是涉及到做選擇的,尤其是提到的五個類型:組合、排序、分割、子集、棋盤。這種都可以構建一顆決策樹,那就都可以用回溯算法去解。解之前先自己把決策樹畫出來。
  • 2.整體上套用模板,最大的不同就在於選擇列表的更新,要能夠根據題目中的要求來更新選擇列表,比如到達某個深度了,比如和為某個值了等等;
  • 3.在求和問題中,排序之后加上剪枝是很常見的操作,能夠舍棄無關的操作(和已經到達某一值了,因為排過序,其后的值就更大了);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM