CHAPTER 5 树与二叉树
结点的度:树中一个节点的孩子个数 树的度:该树中结点的度的最大值
树包含的计算:
1.树中的节点数 = 所有节点度数+1
2.度为m的树中第i层至多有mi-1个节点(最多的情况就是m0, m1, m2, …, mi-1)
3.高度为h的m叉树至多有(mh-1)/(m-1)
4.具有n个节点的m叉树的最小高度(上面都填满就行了)
二叉树包含的计算:
1.非空二叉树叶子节点数 = 度为2的节点数+1 n0 = n2 + 1
总节点数 = 分支个数+1
分支个数 = 1度为1的节点数 + 2度为2的节点数
->n0 + n1 + n2 = n1 + 2n2 +1 n0 = n2 + 1
2.非空二叉树第k层至多有2k-1个节点(懂得都懂,自k层以上全部填满)
3.高为h的二叉树至多有2h-1个节点
4.完全二叉树的编号关系:
结点i的爹:i/2下取整
结点i的左儿子:2i
结点i的右儿子:2*i+1
5.完全二叉树中结点i所在的层次: ⌊ log2i ⌋+1
6.具有n个节点的完全二叉树的高度:自己算去
m叉树与度为m的树的区别
1.m叉树是所有结点的度数小于等于m,最大度数可以小于m,甚至可以是空树;而度为m的树至少有一个结点的度是m而且最大的度是m,显然度为m的树不可能为空树
2.度为m的树至少有m+1个结点
二叉树的存储结构:
顺序存储:用数组严格按照从上至下,从左往右的顺序,空节点用特殊值标出。完全二叉树和满二叉树非常适合这种结构
中序遍历的非递归形式:
1.如果该节点左孩子非空,则左孩子压栈
2.如果左孩子为空,则退栈,打印当前节点,遍历节点 = 退出来的右孩子
3.当指针为空并且栈空时,结束while
前序遍历的非递归形式:
1.当前节点非空,则打印之再压栈
2.当前节点空,则退栈,遍历节点 = 退出来栈的节点的右孩子
后序遍历的非递归形式:
先说几个区别于前中俩兄弟的要点:1.getTop但不pop2.增加recent point
1.先边压栈边走到最左边
2.如果左孩子空了,getTop(就是从空的左孩子往上退),看右孩子:
2.1如果右孩子空,就退栈,(指针还是原来的,但是退栈了),现在的节点是recent point(以防他是某人的右孩子),遍历指针 = NULL(以便下次循环往上退)
2.2如果右孩子被记录过了,处理与2.1一样
2.3如果右孩子没空且没记录过,就令当前节点=右孩子
由遍历序列构造二叉树:
先序序列和中序序列可以唯一确定二叉树:
后序序列和中序序列可以唯一确定二叉树
层序序列和中序序列可以唯一确定二叉树
前序序列和后序序列不能唯一确定二叉树!!!
做题方法:前序序列的开头元素就是根节点、后序序列的结尾元素就是根节点。然后根据根节点和中序序列把左半边和右半边划分出来
线索二叉树
传统二叉树只能得到父子关系,不能表示节点在遍历中的前驱和后继,空的指针数为2*n0+n1 = n0+n2+n1+1 = n+1,这些空指针可以用来存放前驱指针和后继指针。
struct typedef Node{
int data;
int ltag;//[0,1] 0表示左指针为左孩子,1表示左指针为前驱
struct Node *lchild;
int rtag;//[0,1] 0表示右指针为右孩子,1表示右指针为后继
struct Node *rchild;
}Node,*BiTree;
线索二叉树:
线索化:在递归式的遍历下Visit的函数中做两件事:1.为当前节点找到先继 2.为前节点找到后继(这里会有条件的,肯定是pre的右孩子为空,不空的不用设)3.前节点 = 当前节点
当有了线索二叉树之后,怎么找到其中某个节点的前驱或者后继?
中序线索二叉树中的中序后继:
1.如果rtag == 1(右孩子就是直接后继)那么后继就是他的rchild
2.如果rtag == 0(右孩子是正常的右孩子)右孩子的最左下
用遍历顺序展开比较直观
中序线索树中的中序前驱:
1.ltag == 1
2.ltag == 0左孩子的最右下
先序线索树中的先序后继
根左右->有左孩子则是左孩子没左孩子则是右孩子
先序线索树中的先序前驱(基于可以知道其父节点是谁的前提)
根左右->推不出来
看他的爹是谁:
1.如果该节点是其父节点的左孩子
根(根左右)右->则其父节点就是他的前驱
2.如果该节点是其父节点的右孩子
根(根左右)(根左右)->前驱为其左兄弟节点左后一个被先序遍历的节点
后序线索树中的后序前驱:
左(左右根)根->有右孩子就是他的右孩子,没有右孩子就是他的左孩子
后序线索树中的后序后继
如果是他爹的左孩子:(左右根)右根,他爹右孩子中后序遍历的第一个节点
如果是他爹的右孩子:左(左右根)根,就是他爹
树、森林
树的三种存储方法:
1.双亲表示法,常用于顺序存储{int data; int parent},parent为他爹在数组中的序号
2.孩子表示法,每一个节点都用单链表串起来
3.孩子兄弟表示法(二叉树表示法),节点值,指向第一个孩子的指针,指向自己下一个兄弟节点的指针
树、森林与二叉树的转换:
树<->二叉树:左孩右兄
森林<->二叉树:与树和二叉树的转换类似,就是把每个根节点用兄弟穿起来
树与森林的遍历:
树先根遍历:先visit根,再依次遍历根节点的每颗子树
树后根遍历:先遍历该节点的每颗子树,最后访问根
先序遍历森林:依次对各个子树先根遍历
中序遍历森林:依次对各个子树后根遍历
哈夫曼树与哈夫曼编码:
给结点赋值:结点的权值
从根节点走了几步走到了这个节点:路径长度
权值*路径长度 = 结点的带权路径长度
一棵树中所有节点的带权路径之和:树的带权路径长度
哈夫曼树:带权路径长度最小的二叉树
哈夫曼树的构造:从所有节点中挑出俩权值最小的,然后他俩的根节点加入节点池中
哈夫曼编码(可变长编码):某点的出现次数为其权值,然后根据这些权值构造哈夫曼树,在树中,左边是0,右边是1(没有编码是另一个编码的前缀)