二叉樹(Binary Tree)
數據結構與算法筆記目錄:《戀上數據結構》 筆記目錄
想加深 Java 基礎推薦看這個: Java 強化筆記目錄
簡單的看一下樹形結構:
生活中的樹形結構:
- 使用樹形結構可以大大提高效率;
- 樹形結構是算法面試的重點;
樹(Tree)的基本概念
節點、根節點、父節點、子節點、兄弟節點;
- 一棵樹可以沒有任何節點,稱爲空樹
- 一棵樹可以只有 1 個節點,也就是隻有根節點
子樹、左子樹、右子樹;
節點的度(degree):子樹的個數;
樹的度:所有節點度中的最大值;
葉子節點(leaf):度爲 0 的節點;
非葉子節點:度不爲 0 的節點;
層數(level):根節點在第 1 層,根節點的子節點在第 2 層,以此類推(有些教程也從第 0 層開始計算)
節點的深度(depth):從根節點到當前節點的唯一路徑上的節點總數;
節點的高度(height):從當前節點到最遠葉子節點的路徑上的節點總數;
樹的深度:所有節點深度中的最大值;
樹的高度:所有節點高度中的最大值;
數的深度 等於 樹的高度;
有序樹、無序樹、森林
有序樹:樹中任意節點的子節點之間有順序關係;
無序樹:樹中任意節點的子節點之間沒有順序關係,也稱爲 “自由樹”;
森林:由 m(m ≥ 0)棵互不相交的樹組成的集合;
二叉樹(Binary Tree)
二叉樹的特點:
- 每個節點的度最大爲 2(最多擁有 2 棵子樹)
- 左子樹和右子樹是有順序的,二叉樹是有序樹
- 即使某節點只有一棵子樹,也要區分左右子樹
二叉樹的性質
非空二叉樹的第 i 層,最多有 2i−1 個節點( i ≥ 1 )
在高度爲 h 的二叉樹上最多有 2h-1 個結點( h ≥ 1 )
對於任何一棵非空二叉樹,如果葉子節點個數爲 n0
,度爲 2 的節點個數爲 n2
,則有:n0 = n2 + 1
- 假設度爲 1 的節點個數爲
n1
,那麼二叉樹的節點總數n = n0 + n1 + n2
- 二叉樹的邊數
T
=n1 + 2 * n2
=n – 1
=n0 + n1 + n2 – 1
- 因此
n0 = n2 + 1
真二叉樹(Proper Binary Tree)
真二叉樹:所有節點的度都要麼爲 0,要麼爲 2
下圖不是真二叉樹:
滿二叉樹(Full Binary Tree)
滿二叉樹:最後一層節點的度都爲 0,其他節點的度都爲 2
假設滿二叉樹的高度爲 h( h ≥ 1 ),那麼
- 第 i 層的節點數量: 2i−1
- 葉子節點數量: 2h−1
- 總節點數量 n
- n = 2h − 1 = 20 + 21 + 22 + ⋯ + 2h−1
- 樹高度與總節點的關係:h = log2(n + 1)
在同樣高度的二叉樹中,滿二叉樹的葉子節點數量最多、總節點數量最多;
滿二叉樹一定是真二叉樹,真二叉樹不一定是滿二叉樹;
完全二叉樹(Complete Binary Tree)
完全二叉樹:對節點從上至下、左至右開始編號,其所有編號都能與相同高度的滿二叉樹中的編號對應
完全二叉樹的性質:
- 度爲 1 的節點只有左子樹
- 度爲 1 的節點要麼是 1 個,要麼是 0 個
- 同樣節點數量的二叉樹,完全二叉樹的高度最小
- 假設完全二叉樹的高度爲 h( h ≥ 1 ),那麼:
- 至少有 2h−1 個節點 ( 20 + 21 + 22 + ⋯ + 2h−2 + 1 )
- 最多有 2h − 1 個節點( 20 + 21 + 22 + ⋯ + 2h−1,即 滿二叉樹 )
- 總節點數量爲 n
2h−1 ≤ n < 2h
h − 1 ≤ log2n < h
h = floor( log2n ) + 1
(floor
是向下取整,ceiling
是向上取整 )
下圖不是完全二叉樹:
面試題(完全二叉樹)
國外教材的說法:瞭解一下
二叉樹的遍歷 + 練習題
遍歷是數據結構中的常見操作:把所有元素都訪問一遍;
線性數據結構的遍歷比較簡單:
- 正序遍歷
- 逆序遍歷
根據節點訪問順序的不同,二叉樹的常見遍歷方式有 4 種:
- 前序遍歷(Preorder Traversal)
- 中序遍歷(Inorder Traversal)
- 後序遍歷(Postorder Traversal)
- 層序遍歷(Level Order Traversal)
遍歷的應用:
- 前序遍歷:樹狀結構展示(注意左右子樹的順序)
- 中序遍歷:二叉搜索樹的中序遍歷按升序或者降序處理節點
- 後序遍歷:適用於一些先子後父的操作
- 層序遍歷:計算二叉樹的高度、判斷一棵樹是否爲完全二叉樹
前序遍歷(Preorder Traversal)
訪問順序:根節點、前序遍歷左子樹、前序遍歷右子樹
下圖前序遍歷的結果是:7、4、2、1、3、5、9、8、11、10、12
二叉樹的前序遍歷:https://leetcode-cn.com/problems/binary-tree-preorder-traversal/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
List<Integer> list = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
if(root == null) return list;
list.add(root.val);
preorderTraversal(root.left);
preorderTraversal(root.right);
return list;
}
}
中序遍歷(Inorder Traversal)
訪問順序:中序遍歷左子樹、根節點、中序遍歷右子樹
下圖中序遍歷的結果是:1、2、3、4、5、7、8、9、10、11、12
另一種中序遍歷訪問順序:中序遍歷右子樹、根節點、中序遍歷左子樹
則下圖的中序遍歷的結果是:12、11、10、9、8 、7、5、4、3、2、1
二叉搜索樹的中序遍歷結果是升序或者降序的;
二叉樹的中序遍歷: https://leetcode-cn.com/problems/binary-tree-inorder-traversal/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
List<Integer> list = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
if(root == null) return list;
inorderTraversal(root.left);
list.add(root.val);
inorderTraversal(root.right);
return list;
}
}
後序遍歷(Postorder Traversal)
訪問順序:後序遍歷左子樹、後序遍歷右子樹、根節點
下圖的後序遍歷的結果是:1、3、2、5、4、8、10、12、11、9、7
二叉樹的後序遍歷: https://leetcode-cn.com/problems/binary-tree-postorder-traversal/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
List<Integer> list = new ArrayList<Integer>();
public List<Integer> postorderTraversal(TreeNode root) {
if(root == null) return list;
postorderTraversal(root.left);
postorderTraversal(root.right);
list.add(root.val);
return list;
}
}
層序遍歷(Level Order Traversal)
訪問順序:從上到下、從左到右依次訪問每一個節點
下圖的層序遍歷的結果是:7、4、9、2、5、8、11、1、3、10、12
二叉樹的層次遍歷: https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
List<List<Integer>> resList = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
if(root == null) return resList;
Queue<TreeNode> queue = new LinkedList<>();
int levelSize = 1;
queue.offer(root);
List<Integer> list = new ArrayList<>(); ;
while(!queue.isEmpty()){
TreeNode node = queue.poll();
list.add(node.val);
levelSize--;
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
if(levelSize == 0){
resList.add(list);
levelSize = queue.size();
list = new ArrayList<>();
}
}
return resList;
}
}
根據遍歷結果重構二叉樹
以下結果可以保證重構出唯一的一棵二叉樹:
- 前序遍歷 + 中序遍歷
- 後序遍歷 + 中序遍歷
前序遍歷 + 後序遍歷:
- 如果它是一棵真二叉樹(Proper Binary Tree),結果是唯一的
- 不然結果不唯一
前序遍歷+中序遍歷 重構二叉樹
前驅節點(predecessor)
後繼節點(successor)
四則運算
四則運算的表達式可以分爲3種:
- 前綴表達式(prefix expression),又稱爲波蘭表達式
- 中綴表達式(infix expression)
- 後綴表達式(postfix expression),又稱爲逆波蘭表達式
表達式樹
練習
翻轉二叉樹
226_翻轉二叉樹:https://leetcode-cn.com/problems/invert-binary-tree/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null) return root;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
TreeNode node = queue.poll();
TreeNode treeNode = node.left;
node.left = node.right;
node.right = treeNode;
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
}
return root;
}
}
二叉樹的最大深度
104_二叉樹的最大深度:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
}