<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>

          七十九、深度和廣度優(yōu)先搜索算法

          共 5815字,需瀏覽 12分鐘

           ·

          2021-01-12 23:08


          「@Author:Runsen」

          ?

          編程的本質(zhì)來源于算法,而算法的本質(zhì)來源于數(shù)學(xué),編程只不過將數(shù)學(xué)題進(jìn)行代碼化。「---- Runsen」

          ?

          深度優(yōu)先搜索和廣度優(yōu)先搜索作為應(yīng)用廣泛的搜索算法,一般是必考算法。

          深度優(yōu)先算法(DFS)

          深度優(yōu)先算法的本質(zhì)是回溯算法,多數(shù)是應(yīng)用在樹上,一個比較典型的應(yīng)用就是二叉樹的中序遍歷。

          DFS的實現(xiàn)考慮要以下幾個問題即可:

          ①.邊界范圍:「即搜索終止條件,遞歸結(jié)束條件?!?/strong>

          ②.可供選擇的范圍列表:「所有可供選擇的范圍列表?!?/strong>

          ③.已做出的選擇列表:「標(biāo)記當(dāng)前已經(jīng)做出的選擇?!?/strong>

          深度優(yōu)先搜索是圖論中的經(jīng)典算法,利用深度優(yōu)先搜索算法可以產(chǎn)生目標(biāo)圖的相應(yīng)拓?fù)渑判虮?,利用拓?fù)渑判虮砜梢苑奖愕慕鉀Q很多相關(guān)的圖論問題,如最大路徑問題等等。一般用堆數(shù)據(jù)結(jié)構(gòu)來輔助實現(xiàn)DFS算法。

          根據(jù)深度優(yōu)先搜索的特點,采用遞歸函數(shù)實現(xiàn)比較簡單。

          廣度優(yōu)先算法(BFS)

          先訪問完當(dāng)前頂點的所有鄰接點,然后再訪問下一層的所有節(jié)點,該算法適用于解決最短、最小路徑等問題,但是構(gòu)建廣度優(yōu)先算法需要維護(hù)自己的隊列。

          比如,二叉樹的層次遍歷,我們大概會有如下幾個步驟:

          • 向Queue中放入root節(jié)點。
          • 只要這個Queue中有元素就一直遍歷。
          • 每次遍歷時,首先計算一下當(dāng)前Queue里有多少個元素,這就是這棵樹當(dāng)前層級元素的數(shù)量,記作Size。
          • 接下來從Queue中移除Size中的多個元素,對他們進(jìn)行符合我們題目的一些操作。
          • 移除每個節(jié)點時,把它們的子節(jié)點添加進(jìn)Queue中。
          • 只要Queue中還有元素,說明還有下一個層級,就一直重復(fù)步驟3去處理下一個層級。

          Leetcode第111題:二叉樹的最小深度

          給定一個二叉樹,找出其最小深度。最小深度是從根節(jié)點到最近葉子節(jié)點的最短路徑上的節(jié)點數(shù)量。

          #?說明:?葉子節(jié)點是指沒有子節(jié)點的節(jié)點。?
          #?示例:?
          #?給定二叉樹?[3,9,20,null,null,15,7],?
          #?????3
          #???/?\
          #??9??20
          #????/??\
          #???15???7?
          #
          #?返回它的最小深度?2.?
          #?Related?Topics?樹?深度優(yōu)先搜索?廣度優(yōu)先搜索

          最大深度:「最大深度是從根節(jié)點到最近葉子節(jié)點的最長路徑上的節(jié)點數(shù)量。」

          最小深度:「最小深度是從根節(jié)點到最近葉子節(jié)點的最短路徑上的節(jié)點數(shù)量?!?/strong>

          對于一個節(jié)點,如果是葉子節(jié)點,則返回其深度;如果只有左子樹,則遞歸得到左子樹的最小深度;如果只有右子樹,則遞歸得到右子樹的最小深度;如果左右孩子節(jié)點都有,則遞歸得到兩個子樹的深度,并且取最小值。

          #?Definition?for?a?binary?tree?node.
          #?class?TreeNode(object):
          #?????def?__init__(self,?x):
          #?????????self.val?=?x
          #?????????self.left?=?None
          #?????????self.right?=?None

          class?Solution(object):
          ????def?minDepth(self,?root):
          ????????"""
          ????????:type?root:?TreeNode
          ????????:rtype:?int
          ????????"""

          ????????if?not?root:
          ????????????return?0
          ????????return?self.get_min_depth(root,?0)

          ????def?get_min_depth(self,?node,?depth):
          ????????#?分為四種情況:葉子節(jié)點,只有左孩子的節(jié)點,只有右孩子的節(jié)點,兩個孩子都有的節(jié)點
          ????????if?not?node.left?and?not?node.right:
          ????????????return?depth+1
          ????????if?node.left?and?node.right:
          ????????????return?min(self.get_min_depth(node.left,?depth+1),?self.get_min_depth(node.right,?depth+1))
          ????????if?node.left:
          ????????????return?self.get_min_depth(node.left,?depth+1)
          ????????if?node.right:
          ????????????return?self.get_min_depth(node.right,?depth+1)

          上面的是代碼是遞歸的做法。

          廣度優(yōu)先算法(BFS),當(dāng)遇到第一個葉子節(jié)點的時候,該節(jié)點深度就是最小深度。

          代碼和層次遍歷的代碼很類似。

          from?collections?import?queue
          class?Solution:
          ????def?minDepth(self,?root:?TreeNode)?->?int:
          ????????if?not?root:?return?0
          ????????queue?=?[(1,?root)]
          ????????while?queue:
          ????????????depth,?node?=?queue.pop(0)
          ????????????if?not?node.left?and?not?node.right:
          ????????????????return?depth
          ????????????if?node.left:
          ????????????????queue.append((depth?+?1,?node.left))
          ????????????if?node.right:
          ????????????????queue.append((depth?+?1,?node.right))
          ????????return?0

          首先可以想到使用深度優(yōu)先搜索的方法,遍歷整棵樹,記錄最小深度。

          對于每一個非葉子節(jié)點,我們只需要分別計算其左右子樹的最小葉子節(jié)點深度。這樣就將一個大問題轉(zhuǎn)化為了小問題,可以遞歸地解決該問題。

          DFS,需要把所有的葉子節(jié)點的深度進(jìn)行比較,才可以得到最終的最小深度。

          class?Solution:
          ????def?minDepth(self,?root:?TreeNode)?->?int:
          ????????if?not?root:
          ????????????return?0
          ????????
          ????????if?not?root.left?and?not?root.right:
          ????????????return?1
          ????????min_depth?=??float('inf')?
          ????????if?root.left:
          ????????????min_depth?=?min(self.minDepth(root.left),?min_depth)
          ????????if?root.right:
          ????????????min_depth?=?min(self.minDepth(root.right),?min_depth)
          ????????return?min_depth?+?1

          Leetcode第112題:路徑總和

          給定一個二叉樹和一個目標(biāo)和,判斷該樹中是否存在根節(jié)點到葉子節(jié)點的路徑,這條路徑上所有節(jié)點值相加等于目標(biāo)和。

          #?示例:?
          #給定如下二叉樹,以及目標(biāo)和?sum?=?22,?
          #
          #???????????????5
          #?????????????/?\
          #????????????4???8
          #???????????/???/?\
          #??????????11??13??4
          #?????????/??\??????\
          #????????7????2??????1
          #?返回 true, 因為存在目標(biāo)和為 22 的根節(jié)點到葉子節(jié)點的路徑 5->4->11->2。?
          #?Related?Topics?樹?深度優(yōu)先搜索

          方法一:遞歸(DFS,深度優(yōu)先搜索)

          利用 DFS 找出從根節(jié)點到葉子節(jié)點的所有路徑,只要有任意一條路徑的 和 等于 sum,就返回 True。

          class?Solution:
          ????def?hasPathSum(self,?root:?TreeNode,?sum:?int)?->?bool:
          ????????#?二叉樹?root?為?null
          ????????if?not?root:
          ????????????return?False
          ????????#?無左右子樹
          ????????if?not?root.left?and?not?root.right:
          ????????????return?sum?==?root.val
          ????????return?self.hasPathSum(root.left,?sum?-?root.val)?or?self.hasPathSum(root.right,?sum?-?root.val)

          我們可以想到使用廣度優(yōu)先搜索的方式,記錄從根節(jié)點到當(dāng)前節(jié)點的路徑和,以防止重復(fù)計算。

          collection 為 Python 的內(nèi)建集合版塊,collection.deque 為雙端隊列,可以從兩端添加(append)和彈出(pop)元素,且時間復(fù)雜度為 O(1),耗時小于 list 的 insert 和 pop 操作的 O(N)。

          d?=?deque([1,?2,?3,?4,?5]
          ##?兩端添加元素
          d.append(6)?????????????????#?deque([1,?2,?3,?4,?5,?6])
          d.appendleft(0)?????????????#?deque([0,?1,?2,?3,?4,?5,?6])
          ##?兩端按序拓展元素
          d.extend([7,?8])????????????#?deque([0,?1,?2,?3,?4,?5,?6,?7,?8])
          d.extendleft([-1,?-2])??????#?deque([-2,?-1,?0,?1,?2,?3,?4,?5,?6,?7,?8])
          ##?指定位置插入元素
          d.insert(0,?-3)?????????????#?deque([-3,?-2,?-1,?0,?1,?2,?3,?4,?5,?6,?7,?8])
          ##?兩端彈出元素
          d.pop()?????????????????????#?deque([-3,?-2,?-1,?0,?1,?2,?3,?4,?5,?6,?7])
          d.popleft()?????????????????#?deque([-2,?-1,?0,?1,?2,?3,?4,?5,?6,?7])
          ##?刪除從左到右找到的首個指定元素
          d.remove(-1)????????????????#?deque([-2,?0,?1,?2,?3,?4,?5,?6,?7])
          ##?指定元素計數(shù)
          d.count(3)??????????????????#?1
          ##?返回從左到右找到的首個指定元素的索引
          d.index(5)??????????????????#?6
          ##?逆序排列
          d.reverse()?????????????????#?deque([7,?6,?5,?4,?3,?2,?1,?0,?-2])
          ##?元素雙向循環(huán)指定步數(shù)
          d.rotate(2)?????????????????#?deque([0,?-2,?7,?6,?5,?4,?3,?2,?1])
          d.rotate(-2)????????????????#?deque([7,?6,?5,?4,?3,?2,?1,?0,?-2])

          時間復(fù)雜度:O(N) 空間復(fù)雜度:O(H),H 為樹的高度,最壞情況下,樹成鏈狀,為 O(N)。

          import?collections

          class?Solution:
          ????def?hasPathSum(self,?root:?TreeNode,?sum:?int)?->?bool:
          ????????if?not?root:
          ????????????return?False
          ????????que_node?=?collections.deque([root])????????#?結(jié)點隊列
          ????????que_val?=?collections.deque([root.val])?????#?結(jié)點值隊列
          ????????while?que_node:
          ????????????now?=?que_node.popleft()????????????????#?當(dāng)前結(jié)點
          ????????????temp?=?que_val.popleft()????????????????#?臨時存儲值
          ????????????if?not?now.left?and?not?now.right:
          ????????????????if?temp?==?sum:
          ????????????????????return?True
          ????????????????continue
          ????????????if?now.left:
          ????????????????que_node.append(now.left)
          ????????????????que_val.append(now.left.val?+?temp)
          ????????????if?now.right:
          ????????????????que_node.append(now.right)
          ????????????????que_val.append(now.right.val?+?temp)
          ????????return?False
          ?

          本文已收錄 GitHub,傳送門~[1] ,里面更有大廠面試完整考點,歡迎 Star。

          ?


          Reference

          [1]

          傳送門~: https://github.com/MaoliRUNsen/runsenlearnpy100

          更多的文章

          點擊下面小程序


          - END -

          瀏覽 63
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                    中文字幕亚洲在线 | 国产亚洲无码在线观看 | 亚洲无码视频在线观看 | 操逼逼综合网 | 国产黄色大全 |