二叉树
- 每个结点最多有两颗子树,结点的度最大为2
- 左子树和右子树是有顺序的,次序不能颠倒
- 节点数为n的树 深度至多为n 至少为log2(n+1)向下取整
- 对于任何一棵非空的二叉树,如果叶节点个数为n0,度数为2的节点个数为n2,则有: n0 = n2 + 1
存储二叉树结构的方法一般有三种,数组、链表、游标
满二叉树
高度为h的满二叉树拥有刚刚好(2^h+1 )-1个节点
完全二叉树
若二叉树高度为h,除h层外其他所有层节点个数都达到了最大个数。若h层有叶节点,则所有的叶节点从左到右排列。这就是完全二叉树
-
具有n的结点的完全二叉树的深度为log2n+1.
-
如果有一颗有n个节点的完全二叉树的节点按层次序编号,对任一层的节点i(1<=i<=n)有
- 如果i=1,则节点是二叉树的根,无双亲,如果i>1,则其双亲节点为[i/2],向下取整
- 如果2i>n那么节点i没有左孩子,否则其左孩子为2i
- 如果2i+1>n那么节点没有右孩子,否则右孩子为2i+1
tips: 编号后,左子节点的编号是父节点编号的两倍
二叉树的遍历
前序遍历:根—左—右。
-
递归实现
-
非递归实现
思路:每次访问树的左节点,并将节点入栈。如果左节点为空,就取栈顶出栈,访问栈顶节点的右节点,并继续访问入栈访问左节点。代码如下:
void preOrderTraverse(Tree t) {
Stack s;
Tree tmp = t;
while((tmp != NULL) || !isEmpty(&s)) {
while(tmp != NULL) {
Push(&s, tmp);
visit(&tmp);
tmp = tmp->lchild;
}
if(!isEmpty(&s)) {
Pop(&s, &tmp);
tmp = tmp->rchild;
}
}
}
中序遍历: 左-根-右
-
递归实现
-
非递归实现
- 若其左孩子不为空,则将t入栈,并将t的左孩子设置为当前的t
- 若其左孩子为空,则取栈顶元素并进行出栈操作,访问该结点。然后将当前的t置为栈顶结点的右孩子
- 直到t为空并且栈为空,则遍历结束。
void inOrderTraverse(Tree t) {
Stack s;
Tree tmp = t;
while((tmp != NULL) || !isEmpty(&s)) {
while(tmp != NULL) {
Push(&s, tmp);
tmp = tmp->lchild;
}
if(!isEmpty(&s)) {
Pop(&s, &tmp);
visit(&tmp);
tmp = tmp->rchild;
}
}
}
后序遍历: 左-右-根
-
递归实现
-
非递归实现
确保在访问父节点之前左右子节点都已经被访问。当前节点如果没有子节点,或者当前节点的子节点都被访问的时候,可以访问当前节点。否则将当前节点的子节点入栈。
void postOrderTraverse(Tree t) {
Stack s;
Tree cur = NULL;
Tree pre = NULL;
Push(&s, t);
while(!isEmpty(&s) {
Top(&s, &cur);
if((cur->lchild == NULL && cur->rchild == NULL)) ||
(cur->lchild == NULL && pre != NULL && pre == cur->rchild) ||
(cur->rchild == NULL && pre != NULL && pre == cur->lchild)) {
Pop(&s, &cur);
visit(cur);
pre = cur;
} else {
if(cur->lchild) {
Push(&s, cur->lchild);
}
if(cur->rchild) {
Push(&s, cur->rchild);
}
}
}
}
}
二叉树的创建
-
使用访问序列建立二叉树,如 先序:ABDCEGFHI 中序:DBAEGCHFI
tips: 如果仅仅知道二叉树的先序遍历和后序遍历,无法确定二叉树
-
使用广义表来构造,如:A(B(D), C(E( ,G), F(H,I)))
线索树
n个结点的二叉树有2n个链域,其中真正有用的是n – 1个,其它n + 1个都是空域。 为了充分利用结点中的空域,使得对某些运算更快,如前驱或后继等运算。
二叉树的应用:霍夫曼树
参考这里
https://blog.csdn.net/shuangde800/article/details/7341289
主要有两类:
- 霍夫曼树构造条件判断树,使其比较次数最少
- 霍夫曼编码