标签:二叉树 二叉树存储结构 遍历二叉树 代码实现二叉树 线索二叉树
二叉树(binary tree)是n(n>=0)个结点的有限集合,该集合为空集合称为空二叉树,或者有一个根结点和两棵互不相交的,分别称为树根结点的左孩子树和右孩子树组成.
- 每个结点最多有两棵子树,所以二叉树总没有度大于2的结点
- 左子树和右子树是有顺序的,次数不能任意颠倒
- 即使树中某结点只有一棵子树,也要区分是左子树还是右子树
所有的结点都只有左子树的二叉树称为左斜树;
所有的结点都只有右子树的二叉树称为右斜树;
这两者统称为斜树
在一棵二叉树中,如果所有的分支节点都存在左子树和右子树, 并且所有的叶子都在同一层上,这样的二叉树称为满二叉树.
特点
- 叶子只能出现在最下一层,出现在其它层就不可能达成平衡.
- 非叶子结点的度一定是2.
- 在同样的深度的二叉树中,满二叉树的结点个数最多,叶子数也最多.
对一棵具有n个结点的二叉树按层序编号,如果编号为i(1 <= i <= n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树.
满二叉树一定是完全二叉树, 但完全二叉树不一定是满二叉树
特点
- 叶子结点只能出现在最下两层.
- 最下层的叶子一定集中在左部连续位置.
- 倒数二层,若有叶子节点,一定在右部连续位置
- 如果结点度为1,则结点只有左孩子,即不存在只有右子树的情况
- 同样结点数的二叉树,完全二叉树的深度最小
性质1
在二叉树的第i层上至多有 2^(i-1) 个结点(i>=1)
性质2
深度为k的二叉树至多有 2^(k)-1 个结点(k>=1)
性质3
对任何一棵二叉树T,如果其终端结点数的n0, 度为2的结点数为n2 则n0 = n2 + 1
树T结点的总数为n=n0+n1+n2
分支结点数= n - 1 = n1 + 2*n2性质4
具有n个结点的完全二叉树的深度为└ log2^n ┘ + 1 (└ x ┘表示不大于x的最大整数)
性质5
如果对一棵有n个结点的完全二叉树(其深度为 └ log2^n ┘ + 1 )结点按层序编号(从第1层到 └ log2^n ┘ + 1 层,每层从左到右)对任一结点i(1 <= i <=n)有
- 如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲结点是: └ i/2┘
- 如果2i>n,则结点i无左孩子(结点i为叶子结点);否则器左孩子是结点2i
- 如果2i+1>n,则结点i无右孩子,否则其右孩子是结点2i+1
由于二叉树的严格的定义 可以采用一维数组存储二叉树中的结点,并且结点的存储位置,也就是数组的下标要能提现结点之间的逻辑关系,比如双亲和孩子的关系,左右兄弟的关系等.
对于一般的二叉树,尽管层序编号不能反映逻辑关系, 可把不存在的结点设置为^便可.
顺序存储结构一般只用于完全二叉树
二叉树每个结点做多有两个孩子,所以他设计一个数据域和两个指针域是比较自然的想法,我们称这样的链表为二叉链表.
左孩子指针域 数据域 右孩子指针域 lchild data rchild
二叉树的遍历是指(traversing binary tree): 从根结点出发,按照某种次序一次访问二叉树中所有的结点,使得每个结点被访问一次且仅被访问一次.
1. 前序遍历
若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,在前序遍历右子树.
2. 中序遍历
若二叉树为空,则空操作返回,否则先从根结点开始(并不是先访问根结点),中序遍历根结点的左子树,然后访问根结点,最后中序遍历右子树.
3. 后序遍历
若二叉树为空,则空操作返回,否则先从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点.
4. 层序遍历
若二叉树为空,则空操作返回,否则先从树的第一层,也就是从根结点开始访问,从上而下逐层访问,在同一层中,按从左到右的顺序对结点逐个访问.
四种遍历方式,都是把树中的结点变成某种意义的线性序列,给程序的实现带来了好处
/*
二叉树(binary tree) - 链式存储结构
实现 二叉树的创建和遍历
遍历和创建分为三种 前序,中序,后序
输入 数据参考 <AB D C > 空格是要输入 的
*/
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0;
typedef int Status;
typedef char TElemType;
//结点定义
typedef struct BiTNode {
TElemType data; //数据域
struct BiTNode *lchild, *rchild; //左右孩子指针
}BiTNode, *BiTree;
//前序遍历(previous order traverse)
void preOrderTraverse (BiTree T){
if(T == NULL){
return;
}
printf("%c ", T->data); //第一步 显示结点数据
preOrderTraverse(T->lchild); //第二步 前序遍历左子树
preOrderTraverse(T->rchild); //第三步 前序遍历右子树
}
//中序遍历(intermediate order traverse)
void inOrderTraverse (BiTree T){
if(T == NULL){
return;
}
inOrderTraverse(T->lchild); //第一步 中序遍历左子树
printf("%c ", T->data); //第二步 显示结点数据
inOrderTraverse(T->rchild); //第三步 中序遍历右子树
}
//后序遍历(post order traverse)
void postOrderTraverse(BiTree T){
if(T == NULL){
return;
}
postOrderTraverse(T->lchild);
postOrderTraverse(T->rchild);
printf("%c ", T->data);
}
//二叉树的建立 按前序输入二叉树中结点的值 (一个字符)
//‘ ‘表示空树 让每个结点确定是否有左右孩子,构造二叉链表表示二叉树
//如果输入顺序按中序或后序 , 代码的66 67 68行顺序要换一下 ,可以参照遍历的代码这里就不实现了.
void createBiTree(BiTree *T){
TElemType e;
scanf("%c",&e);
if(e == ‘ ‘){
*T = NULL;
}
else{
*T = (BiTree)malloc(sizeof(BiTNode)); //生成结点
if(!(*T)){ //创建失败
printf("结点申请失败\n");
return;
}
(*T)->data = e; //给结点赋值
createBiTree(&(*T)->lchild); //构造左子树
createBiTree(&(*T)->rchild); //构造右子树
}
}
int main(void){
BiTree T;
TElemType e;
int option = 1;
printf("\n 1.前序创建二叉树\n 2.前序遍历二叉树\n 3.中序遍历二叉树\n 4.后序遍历二叉树\n 0.退出\n");
while(option){
scanf("%d", &option);
fflush(stdin); //刷新缓冲区 这个是必须的 不然创建的时候输入数据会让你疑惑
switch(option){
case 1:
createBiTree(&T);
printf("二叉树创建成功\n");
break;
case 2:
preOrderTraverse(T);
break;
case 3:
inOrderTraverse(T);
break;
case 4:
postOrderTraverse(T);
break;
case 0:
return OK;
}
}
return OK;
}
注意: 建立的时候 每个结点确定是否有左右孩子,如果没有则用空格代替
缺点
浪费空间,有很多的空指针域 最有有n+1个空指针
二叉树的建立和遍历都是递归的原理
建立 是在原来的打印结点的地方,改成了生成节点,给结点赋值的操作而已.n个结点有2n个指针域,而n个结点的二叉树一共有n-1条分支线,所以空指针域 = 2n-(n-1) = n+1
指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树
为了解决二叉树中空指针域浪费的问题 将空指针利用起来,指向前驱和后继 这种思想称为线索二叉树
优点
便于查找结点
遍历二叉树效率高
便于超找结点的前驱和后继.
版权声明:本文为博主原创文章,未经博主允许不得转载。
二叉树 二叉树的性质 存储结构 遍历二叉树 C实现二叉树的创建和遍历 线索二叉树
标签:二叉树 二叉树存储结构 遍历二叉树 代码实现二叉树 线索二叉树
原文地址:http://blog.csdn.net/u010187139/article/details/46778661