【Python 数据结构 10.二叉树】
二叉树是 n(n ≥ 0) 个结点组成的有限集合,这个集合要么是空集(当 n 等于 0 时),要么是由一个根节点和两棵互不相交的二叉树组成,其中这两棵互不相交的二叉树被称为根节点的左子树和右子树如图所示,2 是 1 的左子树,3 是 1 的右子树;同时,4 和 5 分别是 2 的左右子树,6 和 7分别是 3 的左右子树存放当前结点的value值存放当前节点的左孩子存放当前节点的右孩子接收参数 m
目录
等你读懂了相遇的意义,有了隔阂别放弃
—— 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 = val
self.left = left
self.right = right
2.树的定义
Ⅰ、初始化
接收参数 maxNodes,传入结点最大数目
列表推导式:
class Tree:
def __init__(self, maxNodes):
self.root = None
self.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 None
nowNode = 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.left
else:
root = stack.pop()
res.append(root.val)
root = root.right
return 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:
return
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 postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
node = root
while stack or node:
while node:
stack.append(node)
if node.left != None:
node = node.left
else:
node = node.right
node = stack.pop()
res.append(node.val)
if stack and stack[-1].left == node:
node = stack[-1].right
else:
node = None
return res

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)