[BinaryTree] 廣度搜尋BFS vs 深度搜尋DFS

PC Chen
程式乾貨
Published in
4 min readJan 10, 2021

--

寒流來無聊窩在家裡寫 leetcode ,遇到了 101. Symmetric Tree,題目大意就是要判斷一棵二元樹是不是「對稱的」?一開始就想先把所有節點用 BFS 全部都列出來成一個 list,然後再根據二元樹的節點數一定會呈現「1+2+4+8+...」的模式、來把 list 切成 sublist。如果每個 sublist 都呈現對稱,就代表原始的 binary tree 就是對稱。

但可想而知這個方法行不通,因為不是所有二元樹都是滿節點,這樣會導致切 sublist 的時候就發生錯誤。這個時候靈光一閃:當初旁聽資料結構時有講到另外一個 DFS,可以選擇對二元樹 先向左 or 先向右 地往下優先走訪。

如果向左dfs與向右dfs出來的結果都一樣,就代表這棵二元樹是對稱的~

所以我說什麼是 BFS 與 DFS?

其實就是針對二元樹每一個節點,可以有的不同走訪方式~

BFS v.s DFS

廣度優先搜尋 Breadth-First Search, BFS

直覺的圖示就是一層一層剝洋蔥的方式來走訪節點, 上圖返回的結果就會是 [0, 1, 2, 3, 4, 5, 6]

深度優先搜尋 Depth-First Search, DFS

走訪方式改為優先向下鑽研,下圖的例子是選擇「先往左節點」向下鑽研,因此返回的結果會是 [0, 1, 3, 4, 2, 5, 6]

在實作之前,先基礎介紹 python 中如何造出二元樹,leetcode 中有幫忙簡單定義 class TreeNode,基本上用 TreeNode 就可以定義出一棵二元樹:

上面程式碼中的 r 這個根節點,就代表了下面這棵二元樹:

暫停一下

對二元樹以及走訪方式有基本理解後,沒有接觸過資料結構的讀者們可以先知道 queue(佇列)stack(堆疊) 的概念,再往下看~

BFS Approach: Queue

為了使用 BFS走訪,我們必須使用 queue(佇列),核心概念為利用它 先進先出FIFO 的特性(就跟你排隊買東西一樣)。

首先,我們把 root 放到 queue 中,dequeue 就把該節點的 value 放入 result中,並且規定只要 dequeue 的時候、就要把該節點的左右子節點也放到 queue 中(先放左、在放右),當然如果子節點是空值就不需要放入。

最後,寫個 while loop 規定只要 queue 中還有值,就一直 dequeue、直到裡面完全沒有值,流程圖可參考以下:

BFS Approach: Queue

程式碼實作爲以下,記得 r 輸入圍剛剛上面的範例,輸出結果即為 [1,2,2,3,4,4,3]

# 輸出結果
[1, 2, 2, 3, 4, 4, 3]

DFS Approach: Stack

使用 DFS走訪,我們必須使用 stack(堆疊),核心概念為利用它 先進後出FILO 的特性(就像碟碗盤一樣)

首先,一樣把 root 放到 stack 中,然後在 de-stack 時把該 node 的值放入 result 中。這時要注意的是:de-stack 一樣要同時把開node的左、右子節點放入 stack 中,但這時 先放左 先放右 會影響向下走往的方向。記得:

先放左,則走訪方式為向右下方DFS
先放右,則走訪方式為向左下方DFS

所以聰明的你想一下~我們應該要先放左還是先放右?

對~我們要 先放右結點 喔,這樣才是向左DFS,可參考以下:

DFS Approach: Stack

程式碼中我把向左鑽、向右鑽都寫出來了,可以看出差別就只在於每次放入 stack 中是先放左子節點還是右子節點。

另外也可以看出,我一開始的例子 r 本身就是一個「對稱」的二元樹,所以不管往哪邊dfs結果都一樣會是 [1,2,3,4,2,4,3]

# 輸出結果
[1,2,3,4,2,4,3]
[1,2,3,4,2,4,3]

--

--

PC Chen
程式乾貨

喜歡接觸與動手實作各種軟體技術的後端數據工程師 A data- backend engineer who is enthusiastic in learning and implementing any techniques in software engineering.