版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011815404/article/details/80680967 </div>
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
<div class="htmledit_views" id="content_views">
<h1><a name="t0"></a>【基本概念】</h1>
二叉树( binary tree)是一种特殊的树型结构,它是度数为2的树,即二叉树的每个结点最多有两个子结点。
每个结点的子结点分别称为左孩子、右孩子,它的两棵子树分别称为左子树、右子树。
二叉树有5种基本形态:
【二叉树的性质】
1.在二叉树的第 i 层上最多有 2^(i-1) 个结点(i>=1)
2.深度为 k 的二叉树至多有 2^k –1个结点(k>=1)
特别:一棵深度为 k 且有 2k–1 个结点的二叉树称为满二叉树,其特点是每层上的结点数都是最大结点数。
对满二叉树的结点进行连续编号,约定编号从根结点起,自上而下,从左到右,由此引出完全二叉树的定义:深度为 k,有 n 个结点的二叉树当且仅当其每一个结点都与深度为 k 的满二叉树中编号从 1 到 n 的结点一一对应时,称为完全二叉树。
3.对任意一棵二叉树,如果其叶结点数为 n0,度为 2 的结点数为 n2,则一定满足:n0=n2+1
4.具有 n 个结点的完全二叉树的深度为 floor(log2n)+1
5.对于一棵具有 n 个结点的完全二叉树,对任一个结点(编号为i),有:
1)如果 i=1,则结点 i 为根,无父结点
如果 i>1,则其父结点编号为 i/2
2)如果 2*i>n,则结点 i 无左孩子,否则左孩子编号为2*i
如果2*i+1>n,则结点 i 无右孩子,否则右孩子编号为2*i+1
【二叉树的存储结构】
1.链式存储结构
使用单链表结构或双链表结构
在二叉树的链接存储中,通常采用的方法是,在每个结点中设置3个域:值域、左指针域和右指针域。其结点结构为:
链接存储的另一种方法是:在上面的结点结构中再增加一个parent指针域,用来指向其双亲结点。这种存储结构既便于查找孩子结点,也便于查找双亲结点,当然也带来存储空间的相应增加。
2.顺序存储结构
顺序存储一棵二叉树时,首先对该树中的每个结点进行编号,然后以各结点的编号为下标,把各结点的值对应存储到一个一位数
组中。每个结点的编号与等深度的满二叉树中对应结点的编号相等,即树根结点的编号为1,接着按照从上到下和从左到右的次
序,若一个结点的编号为i,则左、右孩子的编号分别为2i和2i+1。如图,各结点上方的数字就是该结点的编号。
假设分别采用一维数组data1和data2来顺序存储上图的两棵二叉树,则两数组中各元素的值如下图所示。
在二叉树的顺序存储结构中,各结点之间的关系是通过下标计算出来的,因此访问每一个结点的双亲和左、右孩子都非常方便。如对于编号为i的结点,其双亲结点的下标为⌊i/2⌋,若存在左孩子,则左孩子结点的下标为2i,若存在右孩子,则右孩子结点的下标为2i+1。
二叉树的顺序存储结构对于存储完全二叉树是合适的,它能够充分利用存储空间,但对于一般二叉树,特别是对于那些单支结点较多的二叉树来说是很不合适的,因为可能只有少数存储位置被利用,而多数或绝大多数的存储位置空间着。
【二叉树的基本操作】
1.遍历二叉树
二叉树的遍历:按一定的规律和次序访问树中的各个结点,而且每个结点仅被访问一次。
二叉树的遍历问题:在树中查找具有某种特征的结点,或对所有结点逐一进行某种处理。
遍历一般按照从左到右的顺序,分为三种方法:先序遍历、中序遍历、后序遍历
注:已知先序序列和中序序列可以确定出二叉树,已知中序序列和后序序列也可以确定出二叉树
1)先序遍历
若二叉树为空,则空操作,否则:先访问根结点,再先序遍历左子树,然后先序遍历右子树
-
void preorder(tree bt)//先序遍历根结点为bt的二叉树的递归算法
-
{
-
if(bt)
-
{
-
cout << bt->data;
-
preorder(bt->lchild);
-
preorder(bt->rchild);
-
}
-
}
以下图为例,先序遍历此图结果为:124753689
2)中序遍历
若二叉树为空,则空操作,否则:先中序遍历左子树,再访问根结点,然后中序遍历右子树
-
void inorder(tree bt)//中序遍历根结点为bt的二叉树的递归算法
-
{
-
if(bt)
-
{
-
inorder(bt->lchild);
-
cout << bt->data;
-
inorder(bt->rchild);
-
}
-
}
以下图为例,中序遍历此图结果为:742513869
3)后序遍历
若二叉树为空,则空操作,否则:先后序遍历左子树,再后序遍历右子树,然后访问根结点
-
void postorder(tree bt) //后序遍历根结点为bt的二叉树的递归算法
-
{
-
if(bt)
-
{
-
postorder(bt->lchild);
-
postorder(bt->rchild);
-
cout << bt->data;
-
}
-
}
以下图为例,后序遍历此图结果为:745289631
2.建立二叉树
-
void pre_crt(tree &bt)//按先序次序输入二叉树中结点的值,生成
-
{
-
char ch;
-
ch = getchar();
//二叉树的单链表存储结构,bt为指向根结点的指针,'$'表示空树
-
if(ch !=
'$')
-
{
-
bt =
new node;
//建根结点
-
bt->data = ch;
-
pre_crt(bt->lchild);
//建左子树
-
pre_crt(bt->rchild);
//建右子树
-
}
-
else
-
bt =
NULL;
-
}
3.删除二叉树
-
void dis(tree &bt)//删除二叉树
-
{
-
if(bt)
-
{
-
dis(bt->lchild);
//删左子树
-
dis(bt->rchild);
//删右子树
-
delete bt;
//释放父结点
-
}
-
}
4.插入一个结点到排序二叉树中
-
void insert(tree &bt, int n)//插入一个结点到排序二叉树中
-
{
-
if(bt)
-
{
-
if(n < bt->data)
-
insert(bt->lchild, n);
-
else
if(n > bt->data)
-
insert(bt->rchild, n);
-
}
-
else
-
{
-
bt =
new node;
//新开一个空间
-
bt->data = n;
-
bt->lchild = bt->rchild =
NULL;
-
}
-
}
5.在排序二叉树中查找一个数,找到返回该结点,否则返回NULL
-
tree findn(tree bt, int n) //在二叉树中查找一个数,找到返回该结点,否则返回NULL。
-
{
-
if(bt)
-
{
-
if(n < bt->data)
-
findn(bt->lchild, n);
-
else
if(n > bt->data)
-
findn(bt->rchild, n);
-
else
-
return bt;
-
}
-
else
-
return
NULL;
-
}
6.用嵌套括号表示法输出二叉树
-
void print(tree bt)//用嵌套括号表示法输出二叉树
-
{
-
if(bt)
-
{
-
cout << bt->data;
-
if(bt->lchild || bt->rchild)
-
{
-
cout <<
'(';
-
print(bt->lchild);
-
if(bt->rchild)
-
cout <<
',';
-
print(bt->rchild);
-
cout <<
')';
-
}
-
}
-
}
【树的计数问题】
相似二叉树:两者都为空树或者两者均不为空树,且它们的左右子树分别相似。
等价二叉树:两者不仅相似,而且所有对应结点上的数据元素均相同。
二叉树的计数问题就是讨论具有 n 个结点、互不相似的二叉树的数目 Bn 。
一般情况,一棵具有 n(n>1) 个结点的二叉树可以看成是由一个根结点、一棵具有 i 个结点的左子树和一棵具有 n-i-1 个结点的右子树组成,其中0<=i<=n-1,当n很小时,易得:B0=1,B1=1,B2=2,B3=5
由此易得如下递归公式:
化为函数,即得:
【例题】
*二叉树的存储结构摘自https://www.cnblogs.com/jim-blogs/p/9064986.html*