二叉树是一个连通的无环图,并且每一个顶点的度不大于3。有根二叉树还要满足根结点的度不大于2。有了根结点之后,每个顶点定义了唯一的父结点,和最多2个子结点。
性质:
性质1:在二叉树的第i层上至多有2^(i-1)个结点(i≥1)
。
性质2:深度为k的二叉树至多有2^k-1个结点(k≥1)
。
性质3:对任何一棵二叉树T,如果其终端结点数为n1,度为2的结点数为n2,n1=n2+1
。
性质4:具有n个结点的完全二叉树的深度为[log2n]+1 ([x]表示不大于x的最大整数)
。
存储结构:
typedef int datatype;
typedef struct BiTNode
{
datatype data;
struct BiTNode *lchild, *rchild;//左右孩子指针
}node,*tree;
遍历二叉树
规则是若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。
void preordertraverse(tree t) //前序遍历算法
{
if (t == NULL)
return;
cout << t->data <<endl; //可以插入其他操作
preordertraverse(t->lchild);
preordertraverse(t->rchild);
}
规则是若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。
void inordertraverse(tree t)
{
if (t == NULL)
return;
inordertraverse(t->lchild); //中序遍历左子树
cout << t->data << endl;
inordertraverse(t->rchild);//中序遍历右子树
}
规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。
void postordertraverse(tree t)
{
if (t == NULL)
return;
postordertraverse(t->lchild); //后序遍历左子树
postordertraverse(t->rchild);//后序遍历右子树
cout << t->data << endl;
}
层序遍历
就是按照二叉树的层次来输出
int print_at_level(tree t, int level)
{
if (!t || level < 0)
return 0;
if (level == 0) {
cout << t->data;
return 1;
}
return print_at_level(t->lchild, level - 1) + print_at_level(t->rchild, level - 1);
}
int main() {
tree t_;
t_ = NULL;
createbitree(&t_);//前序创建二叉树
for (int i = 0;; i++)
{
if (!print_at_level(t_, i))
break;
}
}
在这里经常会遇到考试问题,已知二叉树的前/后序遍历和中序遍历,如何得到二叉树?
利用前序遍历创建
void createbitree(tree *t)//利用前序来创建一个树,t为二级指针变量
{
datatype a;
cin >> a;
if (a == ‘#‘)
*t = NULL;
else
{
*t= (tree)malloc(sizeof(node));
if (!*t)
return;
(*t)->data = a;
createbitree(&(*t)->lchild);//构造左树
createbitree(&(*t)->rchild);//构造右树
}
}
中序,后续遍历生成二叉树只需改变递归顺序即可。
考虑到二叉链表中的2n个指针域中有n+1个空指针域,为充分利用空间,可以用这些空指针域来保存前驱和后继指针信息。指向该线性序列中的“前驱”或"后继”的指针,称作“线索”(以区别二叉链表中的指向孩子结点的指针,利用二叉链表中的空指针做线索)。
对线索中节点的约定
增加两个标志值:
结构定义
typedef char datatype;
typedef enum PointerTag {Link,Thread}PointerTag;
////枚举,Link为0,Thread为1
typedef struct BiThrNode
{
datatype data;
struct BiTNode* lchild, * rchild;
PointerTag LTag;
PointerTag RTag;
};
中序遍历线索化
void inthreading(BiThrTree p)
{
if (p)
{
inthreading(p->lchild);
if (!p->lchild)
{
p->LTag = Thread;
p->lchild = pre;//左儿子指针指向前驱
}
if (!pre->rchild)//前驱没有右儿子
{
pre->RTag = Thread;
pre->rchild = p; //前驱右儿子指针指向后继(当前节点p)
}
pre = p;
inthreading(p->rchild);
}
}
树转换为二叉树
森林转换为二叉树
森林是由若干棵树组成的,所以完全可以理解为,森林中的每一棵树都是兄弟,可以按照兄弟的处理办法来操作。步骤如下:
基本术语:
在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为,从根结点到该结点之间的路径长度与该结点的权的乘积。
树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。
定义 :
赫夫曼树( Huffman树)一又称最优二叉树,它是n个带权叶子结点构成的所有二叉树中,带权路径长度WPL最小的二叉树。
赫夫曼编码:
一般地,设需要编码的字符集为{ d1,d2,....,dn }
,各个字符在电文中出现的次数或频率集合为{ W1,W2,....,Wn}
,以d1,d2,...,dn
作为叶子结点,以W1....Wn
作为相应叶子结点的权值来构造一棵赫夫曼树。规定赫夫曼树的左分支代表0,右分支代表1,则从根结点到叶子结点所经过的路径分支组成的0和1的序列便为该结点对应字符的编码,这就是赫夫曼编码。
原文:https://www.cnblogs.com/lonely-ok/p/12712510.html