<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Leetcode 上的小偷太難了!!

          共 5026字,需瀏覽 11分鐘

           ·

          2021-06-08 08:16

          周末日常。



          題目介紹

          這是 House Robber III。

          上一篇Leetcode:House Robber II

          這道題和之前兩個版本最大不同在于引入了二叉樹。 樹的每個節(jié)點(具體的房子)都有對應的值(金額)。每個節(jié)點有兩種狀態(tài),偷或者不偷。 規(guī)則是不能同時偷父子節(jié)點, 

          比如上圖,如果選擇偷 A 節(jié)點,那么 B  C 就不能偷,既然 B  C 不能偷,那么 DEFG 就可以偷的。 因為 DEFG  A 是子孫關系,而不是父子關系。

          這樣的背景下,求小偷能偷的最大金額?


          暴力遞歸

          我們還是用上圖的那個例子。我們可以直接求出最大金額,偽代碼如下,


          /**
           * Definition for a binary tree node.
           * type TreeNode struct {
           * Val int
           * Left *TreeNode
           * Right *TreeNode
           * }
           */


          // 求當前A節(jié)點能偷的最大金額res
          // 偽代碼
          item := node.Val + node.Left.Left.Val + node.Left.Right.Val +
              node.Right.Left.Val + node.Right.Right.Val
          res := max(item, node.Left.Val+node.Right.val)

          實際中DE  B 的子節(jié)點,DE 也會變成別人的父節(jié)點,甚至是爺爺節(jié)點。

          對于任意一個節(jié)點,求對應能偷的最大值本質(zhì)就是上述的代碼,我們需要改成遞推公式。


          // 遞推公式偽代碼
          func rob(node *TreeNode) int {
            item1 := node.Val + rob(node.Left.Left) + rob(node.Left.Right) +
              rob(node.Right.Left) + rob(node.Right.Right)

            res := max(item1, rob(node.Left)+rob(node.Right))
          }


          然后我們就可以翻譯上面的代碼,


          /**
           * Definition for a binary tree node.
           * type TreeNode struct {
           * Val int
           * Left *TreeNode
           * Right *TreeNode
           * }
           */


          func rob(root *TreeNode) int {
            if root == nil {
              return 0
            }

            money := root.Val

            if root.Left != nil {
              money += (rob(root.Left.Left) + rob(root.Left.Right))
            }

            if root.Right != nil {
              money += (rob(root.Right.Left) + rob(root.Right.Right))
            }

            res := max(money, rob(root.Left)+rob(root.Right))
            return res
          }

          func max(item1, item2 int) int {
            if item1 >= item2 {
              return item1
            }
            return item2
          }

          要是這樣直接運行,Leetcode 一般會報以下錯誤。 

          存在大量重復計算節(jié)點。比如在計算 A 節(jié)點的值時,會計算BC,同時也會計算 DEFG 的值。 這種情況下,當計算 BC 的時候,又會重新計算一遍 DEFG......

          沒什么是加緩存解決不了的


          記憶化遞歸

          它也有個高大上的名字,叫記憶化遞歸

          /**
           * Definition for a binary tree node.
           * type TreeNode struct {
           * Val int
           * Left *TreeNode
           * Right *TreeNode
           * }
           */


          var cache map[*TreeNode]int 

          func init() {
            cache = make(map[*TreeNode]int)
          }

          func rob(root *TreeNode) int {
            if root == nil {
              return 0
            }

            if item, ok := cache[root]; ok {
              return item
            }

            money := root.Val

            if root.Left != nil {
              money += (rob(root.Left.Left) + rob(root.Left.Right))
            }

            if root.Right != nil {
              money += (rob(root.Right.Left) + rob(root.Right.Right))
            }

            res := max(money, rob(root.Left)+rob(root.Right))
            cache[root] = res
            return res
          }

          func max(item1, item2 int) int {
            if item1 >= item2 {
              return item1
            }
            return item2
          }


          動態(tài)規(guī)劃

          那么,如何用動態(tài)規(guī)劃的思想解這道題?

          動態(tài)規(guī)劃最重要的兩步:

          • 定義狀態(tài)

          • 狀態(tài)轉(zhuǎn)移方

          首先是狀態(tài)。 針對每一個節(jié)點,都只有兩種狀態(tài),偷或者不偷。 在這個基礎上,我們可以進一步的保存每一個狀態(tài)偷或者不偷情況下當前最大金額值。

          假設當前是 q 節(jié)點。 我們用 q[0] 表示選擇偷 q 節(jié)點的情況下當前最大金額值。用 q[1] 表示不選擇偷 q 節(jié)點的情況下當前最大金額值。即:

          // 偽代碼
          // 不偷q
          q[0]=xxx
          // 要偷q
          q[1]=xxx


          那么最終我們只需要:


          // 最大值偽代碼
          max(q[0],q[1])

          接著就是狀態(tài)轉(zhuǎn)移方程。

          假設 l  r 分別代表 q 的左右子節(jié)點。 那么,就會有以下公式:

          當偷 q q 的左右子節(jié)點不能被偷,那么偷 q 情況下最大總金額 q 自身的值 +不能被偷的 l  r 位置最大金額值的和。即

          // 偽代碼
          q[0]=q.val+l[1]+r[1]

          當不偷 q ,q 的左右子節(jié)點都能偷。但是不一定要去偷。這取決于要偷和不偷情況下哪個金額更多。即:

          // 偽代碼
          q[1]=max(l[0],l[1])+max(r[0],r[1])

          這樣的話就可以翻譯代碼了

          /**
           * Definition for a binary tree node.
           * type TreeNode struct {
           * Val int
           * Left *TreeNode
           * Right *TreeNode
           * }
           */

          func rob(root *TreeNode) int {
            val := df(root)
            return max(val[0], val[1])
          }

          func df(root *TreeNode) [2]int {
            var res [2]int
            // 遞歸出口
            if root == nil {
              return [2]int{0, 0}
            }

            l, r := df(root.Left), df(root.Right)
            // 選中當前節(jié)點情況下最大值
            res[0] = root.Val + l[1] + r[1]
            // 不選擇當前節(jié)點情況下最大值
            res[1] = max(l[0], l[1]) + max(r[0], r[1])
            return res
          }

          func max(item1, item2 int) int {
            if item1 >= item2 {
              return item1
            }
            return item2
          }



          推薦閱讀


          福利

          我為大家整理了一份從入門到進階的Go學習資料禮包,包含學習建議:入門看什么,進階看什么。關注公眾號 「polarisxu」,回復 ebook 獲?。贿€可以回復「進群」,和數(shù)萬 Gopher 交流學習。

          瀏覽 78
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美三级韩国三级日本三级在线观看 | 我要看一级黄片 | 黄片精品午夜福利在线免费观看豆花视频 | aa色黄视频 | 久在线 |