二叉树
二叉树
定义
在计算机科学中,二叉树是每个节点最多只有两个分支(即不存在分支度大于2的节点)的树结构。通常分支被称作左子树
或右子树
。二叉树的分支具有左右次序,不能随意颠倒。
在图论中的定义
二叉树是一个连通的无环图,并且每一个顶点的度不大于3。有根二叉树还要满足根节点的度不大于2。有了根节点之后,每个顶点定义了唯一的父节点,和最多2个子节点。然而,没有足够的信息来区分左节点和右节点。如果不考虑连通性,允许图中有多个连通分量,这样的结构叫做森林。
二叉树的类型
满二叉树:每个节点要么有0个孩子节点要么有2个孩子节点的二叉树,称为满二叉树。
完美二叉树:一棵深度为
k
,且有2^k − 1
个节点的二叉树,称为完美二叉树。这种树的特点是每一层上的节点数都是最大节点数。完全二叉树:在一颗二叉树中,若除最后一层外的其余层都是满的,并且最后一层要么是满的,要么在右边缺少连续若干节点,则此二叉树为完全二叉树。
退化二叉树:许多节点只有1个孩子节点,其行为表现更像是单链表的二叉树。
二叉搜索树:又称二叉查找树、有序二叉树或排序二叉树,是指一棵空树或者具有下列性质的二叉树:
- 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
- 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
- 任意节点的左、右子树也分别为二叉搜索树;
AVL:AVL树(Adelson-Velsky and Landis Tree)是计算机科学中最早被发明的自平衡二叉搜索树。在AVL树中,任一节点对应的两棵子树的最大高度差为1,因此它也被称为高度平衡树。
红黑树:红黑树(Red–black tree)是每个节点都带有颜色属性的一种自平衡二叉搜索树,其有以下额外要求:
- 节点是红色或黑色;
- 根是黑色;
- 所有叶子都是黑色(叶子是NIL节点);
- 每个红色节点必须有两个黑色的子节点;
- 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点;
二叉树的性质
对于高度为
h
的满二叉树,其节点总数n
和h
的关系为2h + 1 <= n <= 2^(h+1) - 1
。根节点的高度为0。对于一颗完美二叉树,其节点总数为1 + 2 + 4 + … + 2^ℎ = 2^(ℎ+1) − 1
。对于有
n
个节点的完美二叉树,其叶子节点个数为l = (n + 1) / 2
。对于任意一颗非空二叉树,其叶子节点个数
l
与度为2的内部节点个数i2
的关系为l = i2 + 1
。对于有
n
个节点的二叉树,其最小的树高为ℎ = log2(n + 1) − 1
,此时二叉树表现为完全二叉树。对于有
l
个叶子节点的二叉树,其高度至少是ℎ = log2(l)
。对于一颗节点总数为
n
、边总数为e
的非空二叉树,则节点总数与边的关系为e = n - 1
。对于一颗节点总数为
n
的二叉树,其缺失的孩子节点总数为n + 1
。对于有
n
个节点的完全二叉树,其内部节点的个数为⌊n/2⌋
。
二叉树的操作
插入
- 叶子节点
- 内部节点
删除
- 无孩子节点或有1个孩子节点的节点
- 有2个孩子节点的节点
遍历
深度优先遍历(DFS)
- 前序遍历(根左右), 红色●表示其访问顺序: F, B, A, D, C, E, G, I, H
procedure pre_order(node)
if node = null
return
visit(node)
pre_order(node.left)
pre_order(node.right)
procedure pre_order(node)
if node = null
return
stack ← empty stack
stack.push(node)
while not stack.isEmpty()
node ← stack.pop()
visit(node)
// right child is pushed first so that left is processed first
if node.right ≠ null
stack.push(node.right)
if node.left ≠ null
stack.push(node.left)
- 中序遍历(左根右), 绿色●表示其访问顺序: A, B, C, D, E, F, G, H, I
procedure in_order(node)
if node = null
return
in_order(node.left)
visit(node)
in_order(node.right)
procedure in_order(node)
stack ← empty stack
while not stack.isEmpty() or node ≠ null
if node ≠ null
stack.push(node)
node ← node.left
else
node ← stack.pop()
visit(node)
node ← node.right
- 后序遍历(左右根), 蓝色●表示其访问顺序: A, C, E, D, B, H, I, G, F
procedure post_order(node)
if node = null
return
post_order(node.left)
post_order(node.right)
visit(node)
procedure post_order(node)
stack ← empty stack
lastNodeVisited ← null
while not stack.isEmpty() or node ≠ null
if node ≠ null
stack.push(node)
node ← node.left
else
peekNode ← stack.peek()
// if right child exists and traversing node
// from left child, then move right
if peekNode.right ≠ null and lastNodeVisited ≠ peekNode.right
node ← peekNode.right
else
visit(peekNode)
lastNodeVisited ← stack.pop()
广度优先遍历(BFS)
- 层序遍历: F, B, G, A, D, I, C, E, H
procedure level_order(node)
queue ← empty queue
queue.enqueue(node)
while not queue.isEmpty()
node ← queue.dequeue()
visit(node)
if node.left ≠ null
queue.enqueue(node.left)
if node.right ≠ null
queue.enqueue(node.right)
复杂度
时间复杂度
Operation | Average | Worst case |
---|---|---|
Search | O(log(n)) | O(n) |
Insert | O(log(n)) | O(n) |
Delete | O(log(n)) | O(n) |
空间复杂度
Operation | Average | Worst case |
---|---|---|
Space | O(n) | O(n) |
习题
二叉树的遍历方式
144: 二叉树的前序遍历
94: 二叉树的中序遍历
145: 二叉树的后序遍历
102: 二叉树的层序遍历
二叉树的层序遍历
107: 二叉树的层序遍历II
199: 二叉树的右视图
637: 二叉树的层平均值
429: N叉树的层序遍历
515: 在每个树行中找最大值
116: 填充每个节点的下一个右侧节点指针
117: 填充每个节点的下一个右侧节点指针II
二叉树的属性
101: 对称二叉树
104: 二叉树的最大深度
111: 二叉树的最小深度
222: 完全二叉树的节点个数
110: 平衡二叉树
257: 二叉树的所有路径
404: 左叶子之和
513: 找树左下角的值
112: 路径总和
113: 路径总和II
二叉树的修改与构造
226: 翻转二叉树
105: 从中序与先序遍历序列构造二叉树
106: 从中序与后序遍历序列构造二叉树
654: 最大二叉树
617: 合并二叉树
二叉搜索树的属性
700: 二叉搜索树中的搜索
98: 验证二叉搜索树
530: 二叉搜索树的最小绝对差
501: 二叉搜索树中的众数
538: 把二叉搜索树转换为累加树
二叉树公共祖先问题
236: 二叉树的最近公共祖先
235: 二叉搜索树的最近公共祖先
二叉搜索树的修改与构造
701: 二叉搜索树中的插入操作
450: 删除二叉搜索树中的节点
669: 修剪二叉搜索树
108: 将有序数组转换为二叉搜索树
109: 将有序列表转换为二叉搜索树
其他
100: 相同的树
572: 另一棵树的子树
559: N叉树的最大深度