基本概念
二叉树定义
二叉树的递归定义:
- 一颗空树或者单个结点构成的树是一颗二叉树;
- 除此之外,一棵二叉树有两颗互不相交的二叉树(分别称为左子树和右子树)构成。
需要区分二叉树和二次树的概念。二次树一定是二叉树(当然二次树不区分左右孩子),反之不一定成立。因为一棵一边倒的树可以是二叉树,但是二次树必须要求存在一个度为2的结点。
对于满二叉树和完全二叉树只给出形式化描述:满二叉树(full binary tree)是指一棵叶子结点都在树的最下层的二叉树。而完全二叉树(complete binary tree)是指最下面一层的叶子从左往右依次排满。换句话说,完全二叉树的上一层一定是个满二叉树。满二叉树实际上是完全二叉树的一个特殊情况。
对于完全二叉树,当结点有奇数个,则不存在度为1的结点。反之,存在一个度为1的结点(最多只能存在一个)。
二叉树的性质
二叉树的很多性质都能够根据树的性质推出(是树的一种特殊情况)。至于完全二叉树涉及到编号的一些性质,可以简单画图得出。
其中完全二叉树有一条非常重要的性质:
编号为 k 的结点,其左孩子的编号为
2k ,而右孩子 2k+1 。而父母的编号为 ⌊k2⌋ 。
二叉树与普通树的转换
树转化为二叉树
(1)将树中所有相邻的兄弟之间用线连接起来
(2)对树中的每个结点只保留他与长子之间的连线,删除与其他孩子之间的连线。
二叉树转化为普通树
(1)若某个结点是其父母的左孩子,则把该结点的右孩子、右孩子的右孩子等全部用线与其父母连接起来。
(2)删除原二叉树所有双亲结点与右孩子结点之间的连线。
总结
将树转化为二叉树时,实质上是使用了孩子兄弟链法(因为二叉树也是保留两个指针域)。因此,可以认为左孩子是普通树里的孩子链,而右孩子认为是兄弟链。整个的思路大体如此。
森林的处理
森林的处理较为类似,只需要将每棵树转化为二叉树后,将他们的根都当做平行的兄弟来构造一棵新的二叉树(最左边的作为根,其余的依次作为右孩子)。
二叉树分离成森林,则先依次向右找右孩子,将其分离出每一颗树。然后再将每一棵二叉树转化为普通树。
二叉树的存储结构
顺序存储结构
顺序存储结构的核心在于如何使用连续的空间存放一棵二叉树。对于
n
个结点的完全二叉树,可以对结点进行编号,分别为
解决了存储问题之后,下面要解决的问题在于,如何表示父母和孩子之间的逻辑关系。对于完全二叉树很显然根据性质,编号为
k
的结点,其左孩子的编号为
这种存储方式具有较大的缺陷在于,如果树不是很“平衡”,空结点存储
NIL
也要浪费巨大的空间。这种存储方式通常用在二叉堆上面(堆是一种顺序存储的完全二叉树)。
链式存储结构
二叉树的链式存储结构是使用二叉链来存储的。二叉链是指每个结点使用两个指针域lchild
和rchild
分别用来表示左孩子和右孩子。有时候为了寻找父母和祖先较为方便,也会添加一个parent
指针域。
这种存储方式的优点在于空间利用率较高。
一种典型的结点构造方式如下:
template<typename _Ty>
struct BinaryTreeNode{
_Ty data;
BinaryTreeNode * lchild;
BinaryTreeNode * rchild;
};