iT邦幫忙

2022 iThome 鐵人賽

DAY 8
0

今天開始我們來講回溯演算法,聽名字好像很厲害,但其實也只是動態規劃問題的一種解法.

要使用回溯演算法,就是一個決策樹的尋訪過程.

而構成一個決策樹有以下幾個重點.

1.走過的路徑:到目前節點已經做出的選擇

2.選擇清單:接下來可以做出的選擇

3.結束條件:到達了決策樹的底層,無法做更多選擇的情況

大概的架構會像這樣

fun backTrack(路徑,選擇): 答案{
    if(滿足結束條件)
        return 答案
    for(選擇 in 可選擇列表){
        執行選擇 //重點
        backTrack(路徑,可選擇列表)
        撤銷選擇 //重點
    }       
}

直接看還是有點難懂,我們接下來用排列問題來解釋一下

排列問題

這個問題大家在高中應該遇到過,就是排列與組合的那

問題是這樣的: 有三個數字 [1,2,3] 把它們排成一列,請問有多少方法?

高中老師怎麼教的呢?

首先,數列第一位有三種選擇,所以先在算式上寫上 3

然後因為第一位已經固定了,第二位只剩下了兩個選擇 ,所以算式再乘以2

第三位,因為只剩下唯一一個選項了,所以就乘上1

最後結果,是3 * 2 * 1 = 6 ,總共有6種可能.

恩?好像有些關鍵字跟我們上面說的回溯演算法重複了.其實這種做法就是一種回溯演算法

讓我們把樹畫出來

https://github.com/officeyuli/itHome2022/raw/main/day8/%E5%85%A8%E6%8E%92%E5%88%97%E6%A8%B9.drawio.png

(我知道很醜啦)

這棵樹我們稱之為決策樹,因為上面每個節點都在做決策.

比如說我們來看看這個被塗上色的節點

https://github.com/officeyuli/itHome2022/raw/main/day8/%E5%85%A8%E6%8E%92%E5%88%97%E6%A8%B92%E7%AF%80%E9%BB%9E.drawio.png

站在這個節點來看

1.走過的路徑:到目前節點已經做出的選擇
⇒已經做出選擇 “2”

2.選擇清單:接下來可以做出的選擇

⇒可以選擇 “1”或者”3”

3.結束條件:到達了決策樹的底層,無法做更多選擇的情況

⇒還沒有把所有數字都排列完,還能做選擇,所以沒有達到結束條件

我們來看看決策樹的一部分

https://github.com/officeyuli/itHome2022/raw/main/day8/%E5%85%A8%E6%8E%92%E5%88%97%E6%A8%B9%E7%9A%84%E4%B8%80%E9%83%A8%E5%88%86.drawio.png

前序的程式碼,在進入節點前執行,後序的程式碼,則是在離開某個節點後才行.

在我們於決策樹上面遍歷時,要維護節點的屬性正確,就要在這兩個時間點做做手腳.

所以,所謂的做選擇,就是從可選擇的清單中挑出一個選擇,放進路徑之中.而撤銷選擇,就是從路徑之中拿出一個選擇,放回可選擇的清單.

只要在遞迴前做出選擇,遞迴後撤銷選擇.就能夠維護好每個節點的可選擇清單以及路徑.
來看看程式碼吧

fun main(args: Array<String>) {
    var res: MutableList<List<Int>> = LinkedList<List<Int>>()
    val numbers = intArrayOf(1, 2, 3)

    fun backtrack(numbers: IntArray, track: LinkedList<Int>) {
        if(track.size == numbers.size){//全部數字都使用過 結束條件達成
            res.add(LinkedList(track))
            return
        }
        for(i in numbers.indices){
            if(track.contains(numbers[i])){//排除掉已經做過的選擇
                continue
            }
            track.add(numbers[i])//做選擇
            backtrack(numbers,track)//進入下一層決策
            track.removeLast()//撤銷選擇
        }
    }

    fun permute(numbers: IntArray): List<List<Int>> {
        val track: LinkedList<Int> = LinkedList<Int>()
        backtrack(numbers, track)
        return res
    }
    
    println(permute(numbers))
}

執行的結果如下

https://github.com/officeyuli/itHome2022/raw/main/day8/%E5%9F%B7%E8%A1%8C%E7%B5%90%E6%9E%9C.jpg


上一篇
Day 7 : 湊零錢問題 優化版
下一篇
Day 9 : N皇后問題
系列文
從零開始的LeetCode生活,使用Kotlin學習刷題30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言