[算法笔记]二叉树基础
前言:部分資料引自極客大學《數據結構與算法之美》,感謝王爭老師!
參考鏈接:
- https://visualgo.net/en/bst?slide=1
- https://zh.wikipedia.org/wiki/二叉樹#前(先)序、中序、後序遍歷
- https://leetcode-cn.com/problems/binary-tree-inorder-traversal/
- https://time.geekbang.org/column/article/67856
如何表示(或者存儲)一棵二叉樹?
想要存儲一棵二叉樹,我們有兩種方法,一種是基于指針或者引用的二叉鏈式存儲法,一種是基于數組的順序存儲法。
1. 鏈式存儲
每個節點有三個字段,其中一個存儲數據,另外兩個是指向左右子節點的指針。我們只要拎住根節點,就可以通過左右子節點的指針,把整棵樹都串起來。這種存儲方式我們比較常用。大部分二叉樹代碼都是通過這種結構來實現的。
2. 基于數組的順序存儲法
把根節點存儲在下標 i = 1 的位置,那左子節點存儲在下標 2 * i = 2 的位置,右子節點存儲在 2 * i + 1 = 3 的位置。以此類推,B 節點的左子節點存儲在 2 * i = 2 * 2 = 4 的位置,右子節點存儲在 2 * i + 1 = 2 * 2 + 1 = 5 的位置。
思考:根節點存儲在下標為1的位置,而不是0的位置,有可能和二進制位有關。
下表用4位二進制數來表示圖中完全二叉樹的示例:
| 0001 | 0010 | 0011 |
| 0010 | 0100 | 0101 |
| 0011 | 0110 | 0111 |
| 0100 | 1000 | 1001 |
| 0101 | 1010 | 1011 |
可以發現:
左節點下標 = 根節點下標右移1位
右節點下標 = 根節點下標右移1位 + 1
如果根節點下標從0開始,就不好運算了…
如果是非完全二叉樹,且以數組的形式存儲,在相應空白結點處還是會浪費空間的。所以完全二叉樹使用數組來存儲,空間浪費最少。
二叉樹的屬性
關于“樹”,還有三個比較相似的概念:高度(Height)、深度(Depth)、層(Level)。它們的定義是這樣的:
以下圖為例:
王爭老師對于二叉樹的個人理解:
“高度”這個概念,其實就是從下往上度量,比如我們要度量第 10 層樓的高度、第 13 層樓的高度,起點都是地面。所以,樹這種數據結構的高度也是一樣,從最底層開始計數,并且計數的起點是 0。
“深度”這個概念在生活中是從上往下度量的,比如水中魚的深度,是從水平面開始度量的。所以,樹這種數據結構的深度也是類似的,從根結點開始度量,并且計數起點也是 0。
“層數”跟深度的計算類似,不過,計數起點是 1,也就是說根節點位于第 1 層。
二叉樹的遍歷
經典的方法有三種,前序遍歷、中序遍歷和后序遍歷。其中,前、中、后序,表示的是節點與它的左右子樹節點遍歷打印的先后順序。
- 前序遍歷是指,對于樹中的任意節點來說,先打印這個節點,然后再打印它的左子樹,最后打印它的右子樹。
- 中序遍歷是指,對于樹中的任意節點來說,先打印它的左子樹,然后再打印它本身,最后打印它的右子樹。
- 后序遍歷是指,對于樹中的任意節點來說,先打印它的左子樹,然后再打印它的右子樹,最后打印這個節點本身。
這里使用了遞歸的知識,具體代碼實現引自wiki:
前序遍歷
中序遍歷
visit(node)if node.left != null then visit(node.left)print node.valueif node.right != null then visit(node.right)后序遍歷
visit(node)if node.left != null then visit(node.left)if node.right != null then visit(node.right)print node.value
在這個二叉樹中,
前序遍歷的結果:M,G,D,B,A,C,F,E,J,H,I,K,L,S,P,O,N,Q,R,W,U,T,V,X,Z,Y
中序遍歷的結果:A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z
后序遍歷的結果:A,C,B,E,F,D,I,H,L,K,J,G,N,O,R,Q,P,T,V,U,Y,Z,X,W,S,M
對于各個遍歷過程的個人理解:
前序遍歷:
- 用小明家族繼承傳家寶打比喻或許比較形象:繼承家族傳家寶的原則是傳長子不傳幼子,傳后代不傳兄弟 ---- 從祖父,到父親,到小明本人。假設小明絕后(沒有子孫結點),那么小明的遺產就交給他的弟弟小磊(當前節點的 兄弟節點 )。如果小磊也很不幸絕后(好慘),那么就將財產留給他的叔叔,由叔叔傳承給小磊的堂兄弟。 感覺傳完寶物之后一個家族就完蛋了…
中序遍歷:
- 每次取走二叉樹中 最左邊的元素,其中最左邊是字面意思(哈哈)。
就拿這幅圖為例,ABCDEFGHIJKL…是不是一目了然(手動狗頭)
注意根節點永遠在左節點以及其后代的右部,右節點及其后代的左部。
后序遍歷:
假設小明要搭積木城堡,規則是先打好地基,再蓋上樓頂。而且兩個相鄰城堡可以組合成一個大城堡的地基。
還是以這幅圖為例,先打好地基AC,蓋上屋頂B。此時一個城堡搭建好了,可以搭建下一個城堡:先打好地基E,然后蓋上屋頂F。最后蓋第三層D…
用二叉樹表示下述表達式:a+b*(c-d)-e/f (又是取走最左邊元素…同時)
-
先序遍歷 的序列是:-+a*b-cd/ef (傳承家產)
-
中序遍歷 的序列是:a+b*c-d-e/f (摘除最左邊)
-
后序遍歷 的序列是:abcd-*+ef/- (搭城堡)
小結:遍歷二叉樹:L、D、R分別表示遍歷左子樹、訪問根結點和遍歷右子樹,則先(根)序遍歷二叉樹的順序是DLR,中(根)序遍歷二叉樹的順序是LDR,后(根)序遍歷二叉樹的順序是LRD。還有按層遍歷二叉樹。這些方法的時間復雜度都是O(n),n為結點個數。
最后安利一個很好用的二叉樹在線可視化平臺: https://visualgo.net/en/bst?slide=1
總結
以上是生活随笔為你收集整理的[算法笔记]二叉树基础的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [算法笔记] 爬楼梯
- 下一篇: C宏定义-SWAP的妙用