【二叉树特点】
二叉树由根结点,左子树和右子树构成,具有以下几个典型特点:
- 只有一个根节点,每个结点下面最多只有两棵树;
- 在二叉树的第i层上至多有2^(i-1)个结点(i>=1);
- 深度为k的二叉树至多有(2^k) -1个结点(k>=1);
- 对任意一颗二叉树,如果叶子结点数为n0,度为2的结点(结点拥有的子树数就是度)点数为n2,则n0 = n2 + 1;
对于第4个特点,需要特别解释一下,可以从连接各结点之间的分支线角度来看,结点总数设为n,分支线总数为n-1(规律总结一下就晓得了),度为0的结点就是叶子结点的分支线为0,度为1的结点分支线为1,度为2的分支线为2,故得出n-1= n1*1+n2*2,又有结点总数n = n0+n1+n2,所以综合一下就得到了n0=n2+1。 - n个结点的完全二叉树的深度为不大于n的对数的最大值+1,主要是根据第2条特点延伸出来的;
- 对一棵n个结点的完全二叉树,结点按层序编号,对任一结点i(1<=i<=n),都有
如果i=1,结点i就是二叉树的根,无双亲,如果i>1,则其双亲是不大于i/2的结点,例如结点5,其双亲为5/2 =2,就是结点2;
如果2i>n,则结点i无左孩子,结点i就是叶子结点,否则,其左孩子就是2i;
如果2i+1>n,则结点i无右孩子,否则右孩子就是2i+1。
【二叉树分类】
二叉树有5种形态:空二叉树,只有一个根结点,只有左子树,只有右子树,既有左子树又有右子树。
注意特殊二叉树,包括斜树,满二叉树,完全二叉树。斜树就是只有左子树或者只有右子树的情况;满二叉树就是所有的结点都存在左子树和右子树,并且所有的叶子都在一层上;完全二叉树就是按层序编号从1开始,与满二叉树的位置是一一对应,并且结点按序号依次排列没有空档,就是完全二叉树。
满二叉树
完全二叉树
【注意】二叉树一定是完全二叉树,但完全二叉树不一定是满二叉树
【基本结构】
二叉树的顺序存储结构就是用一维数组存储二叉树上的点,并且数组的下标能体现结点之间的逻辑关系。
二叉树的链表存储结构类似于利用单链表形式,但是不同的是结点结构除了数据域,还存储了指向左孩子和右孩子的指针,如下
struct BitTreeNode
{
int data;
struct BitTreeNode *left,*right;
}
在创建二叉树时,先确定好根结点后再依次添加左右结点。一般采用前序方法创建链表就是先根节点,左孩子,再右孩子,顺序创建,其他也可以,代码方面相应的做修改就好。
【遍历分析】
二叉树操作中最重要的操作就是遍历,有三种,前序遍历,中序遍历,后序遍历。前提是树为空时,空操作返回,
前序遍历:先访问根节点,前序遍历左子树,前序遍历右子树;
中序遍历:中序遍历根节点的左子树,然后访问根节点,最后中序遍历根节点的右子树;
后序遍历:从左到右先叶子后结点的方式遍历访问左右子树,最后访问根节点。
举例如下:
前序遍历结果:ABDECF
中序遍历结果:DBEAFC
后序遍历结果:DEBFCA
【测试代码】
#include<stdio.h>
#include<stdlib.h>
typedef struct BinaryTreeNode
{
char data;
struct BinaryTreeNode *left;
struct BinaryTreeNode *right;
}Node;
//按照前序输入二叉树中结点的值
//构建二叉树
void create(Node **T)
{
char ch;
scanf("%c",&ch);
if(ch == '#')
*T = NULL;
else
{
*T = (Node *)malloc(sizeof(Node));
(*T)->data = ch;
create(&(*T)->left);
create(&(*T)->right);
}
}
//后序遍历
void post_order_visit(Node **T)
{
if(*T == NULL)
return ;
else
{
post_order_visit(&(*T)->left);
post_order_visit(&(*T)->right);
printf("%c",(*T)->data);
}
}
//中序遍历
void in_order_visit(Node **T)
{
if((*T) == NULL)
return;
else
{
in_order_visit(&(*T)->left);
printf("%c",(*T)->data);
in_order_visit(&(*T)->right);
}
}
//前序遍历
void pre_order_visit(Node **T)
{
if((*T) == NULL)
return;
else
{
printf("%c",(*T)->data);
pre_order_visit(&(*T)->left);
pre_order_visit(&(*T)->right);
}
}
void main()
{
Node *root=NULL;
create(&root);
printf("后序遍历:");
post_order_visit(&root);
printf("\n");
printf("中序遍历:");
in_order_visit(&root);
printf("\n");
printf("前序遍历:");
pre_order_visit(&root);
printf("\n");
}
【输出】
【二叉树延伸】
对于一个二叉树遍历的问题,通常是已知一个二叉树的两种遍历结果,通过推导得到二叉树的另一种遍历结果,首先要牢牢掌握住前序遍历,中序遍历,后序遍历的方法思想,这样就不难了。
已知前序遍历序列和中序遍历序列,可以唯一确定一棵二叉树;
已知后序遍历序列和中序遍历序列,可以唯一确定一棵二叉树。
但是,已知前序和后序是不能确定一棵二叉树的,可能性太多。
就以上面测试用例中结果来详细分析一下过程:
前序:ABDECF, 中序:DBEAFC
前序是根->左->右,中序是左->根->右
从前序来看A必然是根节点,由中序看,那么A左边的必然是左子树,A右边的必然是右子树,分析左子树元素DBE,前序是BDE,综合中序和前序特点,B必然是DE的根,进而D,E分别为B的左孩子和右孩子,后序结果就是DEB,而FC是右子树,也是结合排序特点,得到F是C的左孩子,C为F的根,后序结果FC。综合根节点A,后序结果就是DEBFCA。