一、二叉树的定义
- 树中节点的度不大于2的有序树,它是最简单且最重要的树的类型。
- 每个节点最多只能有两棵子树,且有左右之分。
二、二叉树的性质
- 在二叉树的第i层上至多有2^(i-1)个结点,至少有1个结点;
- 深度为k的二叉树至多有2^k-1个结点,至少有k个结点;
- 任意一颗二叉树,若度为2的结点有n个,则叶子数必为n+1(用于计算结点数很重要);
- 具有n个结点的完全二叉树深度必为log2n下取整+1;
- 对完全二叉树,若从上至下、从左至右编号,则编号为i的结点,其左孩子编号必为2i,其右孩子编号必为2i+1,其双亲编号必为i/2。
三、特殊形态的二叉树
- 满二叉树:一颗深度为k且有2^k-1个结点的二叉树。
- 完全二叉树:虽然没有充满结点,但要求其有的结点的编号与满二叉树完全对应; 在完全二叉树中,度为1的结点个数为0或1。
四、二叉树的存储
- 顺序存储:按照满二叉树的结点层次编号,依次存放二叉树中的数据元素;
- 链式存储:二叉链表(两个指针域分别为lchild和rchild,结构为lchild-data-rchild;在n个结点的二叉链表中,有n+1个空指针域;有n个结点的二叉树,边有n-1条,则有n-1个指针域被使用)、三叉链表(结构为lchild-data- parent-rchild)
五、构造二叉树
void solve(int n, int* preOrder, int* inOrder, int* outOrder) {
if (n <= 0)
return;
int root = preOrder[0];
int p = n - 1;
while (p >= 0 && inOrder[p] != root) p--;
outOrder[0] = root;
if (p != n - 1)
solve(n - p - 1, preOrder + p + 1, inOrder + p + 1, outOrder + 1);
if (p != 0)
solve(p, preOrder + 1, inOrder, outOrder + n - p);
}
六、二叉树的遍历
- 遍历:指按某条搜索路线遍访每个结点且不重复。
- 常见的遍历算法有:先序遍历、中序遍历、后序遍历;
- 先序遍历:先访问根结点,按照先根顺序遍历左子树,按照先根顺序遍历右子树;
- 中序遍历:按中根顺序遍历左子树,访问根,按中根顺序访问右子树;
- 后序遍历:按后根顺序访问左子树,按后根顺序访问右子树,访问根;
- 这三种算法基本相同,访问路径相同,只是访问结点的时机不同。
七、线索化二叉树
- 线索化二叉树:保留结点的直接前驱和直接后继的信息;
- 具体方法:若结点有左子树,则lchild指向其左孩子,否则指向其直接前驱(即线索),若结点有右子树,则rchild指向其右孩子,否则指向其直接后继(即线索);为避免混淆,增加两个标志域,形成结构:lchild-LTag-data-RTag-rchild;LTag:若为0,lchild域指向其左孩子;若为1,lchild域指向其前驱;RTag:若为0,rchild域指向其右孩子;若为1,rchild域指向其后继。
八、二叉树的应用
- 平衡树:左右子树深度小于等于1;
- 排序树:左小右大;性质:若其左子树非空,则左子树上所有结点的值均小于根结点的值;若其右子树非空,则右子树上所有结点的值均大于其根结点的值
- 判定树:找到第i层元素,需要i次搜索;
- 带权树:路径长度带权值;
- 最优树/霍夫曼树:带权路径最大的树。