DSA - 二叉树
基本概念
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
再遍历右子树的所有节点
在遍历顺序中,根节点是在中间的,所以叫做中序遍历。
前序遍历
首先打印
root
然后遍历左子树的所有节点
再遍历右子树的所有节点
在遍历顺序中,根节点是在最前面的,所以叫做前序遍历。
后序遍历
首先遍历左子树的所有节点
然后遍历右子树的所有节点
最后打印
root
在遍历顺序中,根节点是在最后面的,所以叫做后序遍历。
Morris Traversal
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
Noted
前/中/后序遍历结果都不能单独定义一颗树,前序+后序
也不行,但 前序+中序
或者 后序+中序
就足以定义一颗树了。
二叉树的分类
Full Binary Tree:每个父节点都有两个或者零个子节点,不同于国内满二叉树的定义,国内似乎没有这个概念。
Perfect Binary Tree:每个父节点都有两个子节点,每个叶子节点的深度都一样,也就是每一层的节点数都是最大的,相当于国内满二叉树的概念。
完全二叉树(Complete Binary Tree):
除了最后一层,其余的每一层都是满的,也就是叶子节点只会出现在最后一层和倒数第二层。
最后一层要么是满的,要么在最右边缺少连续的叶子节点。
degenerate or pathological tree: 是指每个节点最多只有一个子节点,可以是左子节点或者右子节点。
skewed binary tree: 是 degenerate tree 的一种,不过所有子节点都是左子节点,或者都是右子节点。
balanced binary tee: 在平衡二叉树中,对于每个节点,它的左子树和右子树的高度差都是 1 或者 0。
Last updated