目錄
一、二叉樹的基本概念
1.二叉樹的定義
2.二叉樹的特點
3.特殊的二叉樹
Ⅰ、斜樹
Ⅱ、滿二叉樹
Ⅲ、完全二叉樹
Ⅳ、完全二叉樹和滿二叉樹的區別
4.二叉樹的性質
5.二叉樹的順序存儲
Ⅰ、完全二叉樹
Ⅱ、非完全二叉樹
Ⅲ、稀疏二叉樹
6.二叉樹的鏈式存儲
7.二叉樹的遍歷概念
8.二叉樹的前序遍歷
9.二叉樹的中序遍歷
10.二叉樹的后序遍歷
11.二叉樹的層序遍歷
二、Python中的二叉樹
1.樹的結點定義
2.樹的定義
Ⅰ、初始化
Ⅱ、根據給定的結點ID從樹結構中獲取對應的結點
Ⅲ、訪問函數,打印元素結點值
Ⅳ、根據數組創建二叉樹
Ⅴ、先序遍歷
Ⅵ、中序遍歷
Ⅶ、后序遍歷
三、實戰
1.144. 二叉樹的前序遍歷
方法一 遞歸
思路與算法
?編輯
方法二 用棧 Stack 實現迭代遍歷
思路與算法
2.94. 二叉樹的中序遍歷
方法一 遞歸
思路與算法
方法二 用棧實現迭代?
思路與算法
3.145. 二叉樹的后序遍歷
方法一 遞歸
思路與算法
方法二 用棧實現迭代?
思路與算法
等你讀懂了相遇的意義,有了隔閡別放棄
????????????????????????????????????????????????????????—— 25.3.8
一、二叉樹的基本概念
1.二叉樹的定義
? ? ? ? 二叉樹是 n(n ≥ 0) 個結點組成的有限集合,這個集合要么是空集(當 n 等于 0 時),要么是由一個根節點和兩棵互不相交的二叉樹組成,其中這兩棵互不相交的二叉樹被稱為根節點的左子樹和右子樹
? ? ? ? 如圖所示,2 是 1 的左子樹,3 是 1 的右子樹;同時,4 和 5 分別是 2 的左右子樹,6 和 7分別是 3 的左右子樹
2.二叉樹的特點
????????二叉樹是一種樹,它有如下幾個特征:
????????① 每個結點最多二棵子樹,即每個結點的孩子結點個數為 0、1、2.
? ? ? ? ② 這兩棵子樹是有順序的,分別叫:左子樹 和 右子樹,就像左手和右手一樣,是不能顛倒
的。
? ? ? ? ③ 如果只有一棵子樹的情況,也需要區分順序,如圖所示:
b 是 a 的左子樹? ? ? ? ?c 是 a 的右子樹
3.特殊的二叉樹
Ⅰ、斜樹
????????所有結點都只有左子樹的二叉樹,被稱為左斜樹
? ? ? ? 所有結點都只有右子樹的二叉樹,被稱為右斜樹
? ? ? ? 斜樹有點類似 線性表,所以線性表可以理解為一種特殊形式的樹
Ⅱ、滿二叉樹
? ? ? ? 對于一棵二叉樹,如果它的所有根結點和內部結點都存在左右子樹,且所有葉子結點都在同一層,這樣的樹就是滿二叉樹
滿二叉樹有如下幾個特點:
? ? ? ? ① 葉子節點一定在最后一層
? ? ? ? ② 非葉子結點的度為 2
? ? ? ? ③ 深度相同的二叉樹中,滿二叉樹的結點個數最多,為 2 ^ h - 1(其中 h 代表樹的深度)
Ⅲ、完全二叉樹
? ? ? ? 對一顆具有 n 個結點的二叉樹,按照層序進行編號,如果編號 i 的結點 和 同樣深度的滿二叉樹中的編號 i 的結點在二叉樹中,位置完全相同則被稱為 完全二叉樹
Ⅳ、完全二叉樹和滿二叉樹的區別
????????滿二叉樹一定是完全二叉樹,而完全二叉樹則不一定是滿二叉樹,完全二叉樹有如下幾個特
點:
? ? ? ? ① 葉子結點只能出現在最下面兩層
? ? ? ? ② 最下層的葉子結點,一定是集中在左邊的連續位置,倒數第二層如果有葉子結點一定集中在右邊的連續位置
????????③ 如果某個結點度為 1,則只有左子樹,即 不存在只有右子樹 的情況
? ? ? ? ④ 同樣結點數的二叉樹,完全二叉樹的深度最小
????????如下圖所示,就不是一棵完全二叉樹,因為5號結點沒有右子樹,但是6號結點是有左子樹的,不滿足上述第 2 點。
4.二叉樹的性質
? ? ? ? ① 二叉樹的第 i (i >= 1) 層上最多 2 ^ (i - 1) 個結點;
? ? ? ? ② 深度為 h 的二叉樹至多 2 ^ h - 1 個結點;
? ? ? ? ③ n個結點的完全二叉樹的深度為 floor(log2n) + 1(其中 floor(x) 代表對 x 取下整);
5.二叉樹的順序存儲
? ? ? ? 二叉樹的順序存儲就是指:利用順序表對二叉樹進行存儲。結點的存儲位置即順序表的索引,能夠體現結點之間的邏輯關系比如父結點和孩子結點之間的關系,左右兄弟結點之間的關系 等。
Ⅰ、完全二叉樹
? ? ? ? 編號代表了順序表索引的絕對位置,映射后如下:
? ? ? ? 為了方便,將順序表索引為 0 的位置留空
? ? ? ? 當知道某個結點在順序表中的索引 x,就可以知道它左右兒子的索引分別為 2x 和 2x + 1.反之,當知道某個結點的索引 x,也能知道其父節點的索引為 floor(x / 2)
Ⅱ、非完全二叉樹
? ? ? ? 對于非完全二叉樹,只需要將對應不存在的結點設置為空即可
? ? ? ? 編號代表了順序表索引的絕對位置,映射后如下:
Ⅲ、稀疏二叉樹
? ? ? ? 對于較為稀疏的二叉樹,就會有如下情況出現,這時候如果用這種方式進行存儲,就比較浪費內存了
????????編號代表了順序表索引的絕對位置,映射后如下:
? ? ? ? 這種情況下,為了提升內存利用率,我們可以采用鏈表進行存儲
6.二叉樹的鏈式存儲
? ? ? ? 二叉樹每個結點至多有兩個孩子結點,所以對于每個結點設置一個數據域(data)?和 兩個指針域(left?和 right) 即可。指針域 分別指向 左孩子結點 和 右孩子結點。
7.二叉樹的遍歷概念
????????二叉樹的遍歷是指從根結點出發,按照某種次序依次訪問二叉樹中的所有結點,使得每個結點訪問一次且僅被訪問一次。
????????對于線性表的遍歷,要么從頭到尾,要么從尾到頭,遍歷方式較為單純。但是樹不一樣,它的每個結點都有可能有兩個孩子結點,所以遍歷的順序面臨著不同的選擇。
????????二叉樹的常用遍歷方法,有以下四種:前序遍歷、中序遍歷、后序遍歷、層序遍歷。
????????編號代表了順序表索引的絕對位置,映射后如下:
8.二叉樹的前序遍歷
????????如果二叉樹為空則直接返回,否則先訪問根結點,再遞歸前序遍歷左子樹,再遞歸前序遍歷右子樹。(根、左、右)前序遍歷的結果如下:a、b、d、g、h、c、e、f、i
9.二叉樹的中序遍歷
????????如果二叉樹為空則直接返回,否則先遞歸中序遍歷左子樹,再訪問根結點,再遞歸中序遍歷右子樹。(左、根、右)中序遍歷的結果如下:g、d、h、b、a、e、c、i、f
10.二叉樹的后序遍歷
????????如果二叉樹為空則直接返回,否則先遞歸后遍歷左子樹,再遞歸后序遍歷右子樹,再訪問根結點。(左、右、根)后序遍歷的結果如下:g、h、d、b、e、i、f、c、a
11.二叉樹的層序遍歷
????????如果二叉樹為空直接返回,否則依次從樹的第一層開始,從上至下逐層遍歷,在同一層中,按從左到右的順序對結點逐個訪問。圖中二叉樹層序遍歷的結果為:a、b、c、d、e、f、g、h、i
二、Python中的二叉樹
1.樹的結點定義
val:存放當前結點的value值
left:存放當前節點的左孩子
right:存放當前節點的右孩子?
class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = right
2.樹的定義
Ⅰ、初始化
接收參數 maxNodes,傳入結點最大數目
列表推導式:
class Tree:def __init__(self, maxNodes):self.root = Noneself.nodes = [TreeNode() for i in range(maxNodes)]self.nodeSize = maxNodes
Ⅱ、根據給定的結點ID從樹結構中獲取對應的結點
# 根據給定的節點ID從樹結構中獲取對應的節點def GetTreeNode(self, id):return self.nodes[id]
Ⅲ、訪問函數,打印元素結點值
# 訪問函數,打印元素結點的值def visit(self, node):print(node.val, end=' ')
Ⅳ、根據數組創建二叉樹
# 傳入一個數組,根據數組創建二叉樹def Create(self, arr, size, nodeId):if nodeId >= size or arr[nodeId] == None:return NonenowNode = self.GetTreeNode(nodeId)nowNode.val = arr[nodeId]nowNode.left = self.Create(arr, size, 2 * nodeId)nowNode.right = self.Create(arr, size, 2 * nodeId + 1)return nowNode
Ⅴ、先序遍歷
# 先序遍歷def PreOrder(self, node):if node != None:self.visit(node)self.PreOrder(node.left)self.PreOrder(node.right)def preOrderTraversal(self):self.PreOrder(self.root)print('')
Ⅵ、中序遍歷
# 中序遍歷def InOrder(self, node):if node != None:self.InOrder(node.left)self.visit(node)self.InOrder(node.right)def InOrderTraversal(self):self.InOrder(self.root)print('')
Ⅶ、后序遍歷
# 后序遍歷def PostOrder(self, node):if node != None:self.PostOrder(node.left)self.PostOrder(node.right)self.visit(node)def PostTraversal(self):self.PostOrder(self.root)print('')
Ⅷ、測試代碼?
def Test():arr = [None, 'a', 'b', 'c', 'd', None, 'e', 'f', 'g', 'h', None, None, None, None, 'i']tree = Tree(len(arr))tree.CreateTree(arr)tree.preOrderTraversal()tree.InOrderTraversal()tree.PostTraversal()Test()
三、實戰
1.144. 二叉樹的前序遍歷
給你二叉樹的根節點?
root
?,返回它節點值的?前序?遍歷。示例 1:
輸入:root = [1,null,2,3]
輸出:[1,2,3]
解釋:
示例 2:
輸入:root = [1,2,3,4,5,null,8,null,null,6,7,9]
輸出:[1,2,4,5,6,7,3,8,9]
解釋:
示例 3:
輸入:root = []
輸出:[]
示例 4:
輸入:root = [1]
輸出:[1]
提示:
- 樹中節點數目在范圍?
[0, 100]
?內-100 <= Node.val <= 100
進階:遞歸算法很簡單,你可以通過迭代算法完成嗎?
方法一 遞歸
思路與算法
- 前序遍歷遵循“根 -> 左 -> 右”的順序。
- 遞歸函數?
preorder
?的核心邏輯是:- 如果當前節點?
root
?不為空,則將其值加入結果列表?ret
。 - 遞歸遍歷左子樹。
- 遞歸遍歷右子樹。
- 如果當前節點?
- 遞歸終止條件是當前節點為空(
root is None
),此時直接返回。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:def preorder(self, root:Optional[TreeNode], ret:List[int]):if root:ret.append(root.val)self.preorder(root.left, ret)self.preorder(root.right, ret)def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:ret = []self.preorder(root, ret)return ret
方法二 用棧 Stack 實現迭代遍歷
思路與算法
- 前序遍歷遵循“根 -> 左 -> 右”的順序。
- 使用棧來模擬遞歸的過程:
- 從根節點開始,將當前節點的值加入結果列表?
res
,并將當前節點入棧。 - 遍歷左子樹,直到左子樹為空。
- 回溯到上一個節點(通過棧彈出),并遍歷其右子樹。
- 從根節點開始,將當前節點的值加入結果列表?
- 重復上述過程,直到棧為空且當前節點為空。
2.94. 二叉樹的中序遍歷
給定一個二叉樹的根節點?
root
?,返回?它的?中序?遍歷?。示例 1:
輸入:root = [1,null,2,3] 輸出:[1,3,2]示例 2:
輸入:root = [] 輸出:[]示例 3:
輸入:root = [1] 輸出:[1]提示:
- 樹中節點數目在范圍?
[0, 100]
?內-100 <= Node.val <= 100
進階:?遞歸算法很簡單,你可以通過迭代算法完成嗎?
方法一 遞歸
思路與算法
- 后序遍歷遵循“左 -> 右 -> 根”的順序。
- 遞歸函數?
PostOrder
?的核心邏輯是:- 如果當前節點?
root
?不為空,則遞歸遍歷其左子樹。 - 遞歸遍歷其右子樹。
- 將當前節點的值加入結果列表?
res
。
- 如果當前節點?
- 遞歸終止條件是當前節點為空(
root is None
),此時直接返回。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:def PostOrder(self, root:Optional[TreeNode], res:List[int]):if root:self.PostOrder(root.left, res)self.PostOrder(root.right, res)res.append(root.val)def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:res = []self.PostOrder(root, res)return res
方法二 用棧實現迭代?
思路與算法
- 中序遍歷遵循“左 -> 根 -> 右”的順序。
- 使用棧來模擬遞歸的過程:
- 從根節點開始,將當前節點入棧,并遍歷其左子樹,直到左子樹為空。
- 回溯到上一個節點(通過棧彈出),將其值加入結果列表?
res
。 - 遍歷其右子樹。
- 重復上述過程,直到棧為空且當前節點為空。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:res, stack = [], []while root or stack:if root:stack.append(root)root = root.leftelse: root = stack.pop()res.append(root.val)root = root.rightreturn res
?
3.145. 二叉樹的后序遍歷
給你一棵二叉樹的根節點?
root
?,返回其節點值的?后序遍歷?。示例 1:
輸入:root = [1,null,2,3]
輸出:[3,2,1]
解釋:
示例 2:
輸入:root = [1,2,3,4,5,null,8,null,null,6,7,9]
輸出:[4,6,7,5,2,9,8,3,1]
解釋:
示例 3:
輸入:root = []
輸出:[]
示例 4:
輸入:root = [1]
輸出:[1]
提示:
- 樹中節點的數目在范圍?
[0, 100]
?內-100 <= Node.val <= 100
進階:遞歸算法很簡單,你可以通過迭代算法完成嗎?
方法一 遞歸
思路與算法
- 后序遍歷遵循“左 -> 右 -> 根”的順序。
- 遞歸函數?
postOrder
?的核心邏輯是:- 如果當前節點?
root
?不為空,則遞歸遍歷其左子樹。 - 遞歸遍歷其右子樹。
- 將當前節點的值加入結果列表?
res
。
- 如果當前節點?
- 遞歸終止條件是當前節點為空(
root is None
),此時直接返回。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:def postOrder(self, root:TreeNode, res):if root is None:returnself.postOrder(root.left, res)self.postOrder(root.right, res)res.append(root.val)def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:res = []self.postOrder(root, res)return res
方法二 用棧實現迭代?
思路與算法
- 后序遍歷遵循“左 -> 右 -> 根”的順序。
- 使用棧來模擬遞歸的過程:
- 從根節點開始,將當前節點入棧,并遍歷其左子樹,直到左子樹為空。
- 如果左子樹為空,則遍歷其右子樹。
- 回溯到上一個節點(通過棧彈出),將其值加入結果列表?
res
。 - 如果當前節點是棧頂節點的左子節點,則繼續遍歷棧頂節點的右子樹;否則,結束當前分支的遍歷。
- 重復上述過程,直到棧為空且當前節點為空。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:res = []stack = []node = rootwhile stack or node:while node:stack.append(node)if node.left != None:node = node.leftelse:node = node.rightnode = stack.pop()res.append(node.val)if stack and stack[-1].left == node:node = stack[-1].rightelse:node = Nonereturn res