Terminologies
- Node: 至少有一个子节点的叫 internal node,没有子节点的叫 leaf node 或者 external node
- Edge: 两个节点之间的连线
- Root
- Height of a Node: 从这个节点到最深的叶子节点一共有多少条边,也就是以这个节点为根节点的树的最大深度
- Depth of a Node: 从根节点到这个节点一共有多少条边
- Height of a Tree: 从根节点到最底下的叶子节点一共有多少条边
- Degree of a Node: 这个节点的分支数
- Forest: 没有关联的若干棵树
树的应用
- 二叉搜索树(BST):用来查询一个集合内是否存在某个元素
- 堆(Heap):用于堆排序
- Trie:树的一种变体,用于路由器中存储路由信息
- B 树、T 树:用于数据库
- AST、CST:用于编译
- 首先遍历左子树的所有节点
- 打印
root
- 再遍历右子树的所有节点
在遍历顺序中,根节点是在中间的,所以叫做中序遍历。
inorder(root.left)
print(root)
inorder(root.right)
- 首先打印
root
- 然后遍历左子树的所有节点
- 再遍历右子树的所有节点
在遍历顺序中,根节点是在最前面的,所以叫做前序遍历。
print(root)
preorder(root.left)
preorder(root.right)
- 首先遍历左子树的所有节点
- 然后遍历右子树的所有节点
- 最后打印
root
在遍历顺序中,根节点是在最后面的,所以叫做后序遍历。
postorder(root.left)
postorder(root.right)
print(root)
O(1) 空间实现遍历
中序遍历思路
使用一个指针,从 root
开始,
- 如果
root.left
为空,那就直接打印root
,然后指针移动到root.right
并开始遍历右子树。 - 如果
root.left
不为空,那我们需要先去遍历左子树,遍历完之后再打印root
,然后再去遍历右子树。
那么问题来了,如果我们直接把指针移动到 root.left
,那左子树遍历完之后我们就没办法回到 root
了,所以在这之前,我们需要把 root
保存起来,保存到哪里呢?
答案是 左子树的最右子节点.right
。
为什么呢?我们来看下中序遍历的顺序: left->root->right
,右子节点是最后遍历的,所以我们在遍历左子树时,结束的地方就是它最右边的子节点,这时也是我们要回到 root
的时候。所以,我们事先把 root
存在这里(也就是把左子树的最右子节点的 right
指针指向 root
),然后我们就可以放心地把指针移动到 root.left
开始遍历左子树了。
那这样岂不是会陷入死循环?所以还要加一个判断,如果,我们去找 root
左子树的最右子节点时,找到的却是 root
本身,说明左子树已经被遍历过了,这时我们需要断开 左子树最右子节点
与 root
之间的关联,然后再依次打印 root
和遍历右子树。
JavaScript Code
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var inorderTraversal = function (root) {
if (!root) return []
const res = []
let cur = root
while (cur) {
if (!cur.left) {
res.push(cur.val)
cur = cur.right
} else {
// Make cur the rightmost child of its left subtree.
let temp = cur.left
while (temp.right && temp.right !== cur) {
temp = temp.right
}
if (!temp.right) {
temp.right = cur
cur = cur.left
} else {
// If the left subtree has been traversed, restore the tree and start traverse the right subtree
temp.right = null
res.push(cur.val)
cur = cur.right
}
}
}
return res
}
Noted
前/中/后序遍历结果都不能单独定义一颗树,前序+后序
也不行,但 前序+中序
或者 后序+中序
就足以定义一颗树了。
-
Full Binary Tree:每个父节点都有两个或者零个子节点,不同于国内满二叉树的定义,国内似乎没有这个概念。
-
Perfect Binary Tree:每个父节点都有两个子节点,每个叶子节点的深度都一样,也就是每一层的节点数都是最大的,相当于国内满二叉树的概念。
-
完全二叉树(Complete Binary Tree):
- 除了最后一层,其余的每一层都是满的,也就是叶子节点只会出现在最后一层和倒数第二层。
- 最后一层要么是满的,要么在最右边缺少连续的叶子节点。
-
degenerate or pathological tree: 是指每个节点最多只有一个子节点,可以是左子节点或者右子节点。
-
skewed binary tree: 是 degenerate tree 的一种,不过所有子节点都是左子节点,或者都是右子节点。
-
balanced binary tee: 在平衡二叉树中,对于每个节点,它的左子树和右子树的高度差都是 1 或者 0。