文章目录
二叉树的概念和性质
二叉树与一般树型结构主要区别
二叉树五种基本形态
概念
性质
-
一棵非空二叉树的第k层至多有
2^(k-1)
个结点 -
深度为h的二叉树至多有
2^h -1
个结(h>1),即每一层都是2^(k-1)个结点 -
对于任何一棵二叉树,如果其终端结点数为n0,度为2的结点数为n2,则
n0=n2+1
-
满二叉树
概念
-
求满二叉树结点个数问题
-
完全二叉树
概念
如果一棵二叉树扣除其最大层次那层后即成为一棵满二叉树,且层次最大那层的所有结点均向左靠齐,则称该二叉树为完全二叉树
- 完全二叉树性质
- 求完全二叉树结点个数
偶数一定有1个度为1的结点,奇数一定有0个度为1的结点,换言之,完全二叉树度数为1的结点个数最多有1个
例:2,3的双亲结点都是1
二叉树的存储结构
顺序存储
对于一棵具有n个结点的完全二叉树,我们可以按从上到下,从左往右的顺序依次将结点存瑞一维数组中
空的位置用0表示,很明显这样浪费了很多空间,所以一般二叉树的顺序存储这么表示
#define MAXSIZE 20
typedef char datatype;
typedef struct{
datatype data;
int rchild,lchild;
int parent;//存放双亲的坐标
}node;
node tree[MAXSIZE];
int n;//树中实际所含结点个数
int root;//存放根结点的下标
链式存储
每个结点也包含三个域。分别记录该节点的属性值(data)及左右子树的位置
typedef char datatype;//结点属性值的类型
extern char *a;//加个全局变量a,extern表示变量或函数定义在别的文件中
typedef struct node{//二叉树节点类型
datatype data;
struct node *lchild,*rchild;
struct node *parent;
}bintnode;//二叉树
typedef bintnode *bintree;//指向二叉树结点的指针类型
//bintnode *等价struct node*
二叉树的遍历
二叉树的遍历算法(前序,中序,后序)
前序遍历
根结点->左子树->右子树
ABDEFCGH
中序遍历
左子树->根结点->右子树
DBFEAGHC
后序遍历
左子树->右子树->根结点
DFEBHGCA
二叉树遍历算法的特点
前序遍历特点
- 前序首点:根结点
- 前序尾点:最右下方叶子结点
前序遍历特点
- 中序首点:最左下方结点
- 中序尾点:最右下方结点(不太准确)
后序遍历特点
- 后序首点:最左下方叶子结点
- 后序尾点:根结点
创建二叉树的存储结构
按照前序遍历 遍历它的左右指针,若指针为空用#表示,否则新创建一个结点
bintree createbintree()
{
char ch=*a++;//方便为实验中给a赋字符串
bintree t;
if((ch=getchar())=='#')
t=NULL;
else{
t=(bintnode *)malloc(sizeof(bintnode));
t->data=ch;//建立子树根结点
t->lchild=createbintree();
t->rchild=createbintree();
}
return t;
}
前序遍历非递归算法
栈里面那个结点是访问过的,右指针没有被访问
- 采用非递归方式实现二叉树遍历时,必须使用一个堆栈记录回溯点,以便将来进行回溯
- 循环处理的两个条件:```指针不为空或栈不为空`(t||s.top!=-1)``
- 顺序栈实现
void preorder1(bintreet)
{
seqsatck s;//在已经定义栈的情况下
init(&s);
while(t||s.top!=-1)
{
if(t){
printf("%c",t->data);
push(&s,t);//入栈
t=t->lchild;//指向左指针
}
else{
t=pop(&s);//出栈
t=t->rchild;//指向右指针
}
}
}
中序遍历非递归算法
与前序区别:
栈里面那个结点是没有被访问的,右指针没有被访问
只有打印位置的区别
void preorder1(bintreet)
{
seqsatck s;//在已经定义栈的情况下
init(&s);
while(t||s.top!=-1)
{
if(t){//当前子树不为空的情况
push(&s,t);//入栈
t=t->lchild;//指向左指针
}
else{
t=pop(&s);//出栈
printf("%c",t->data);
t=t->rchild;//指向右指针
}
}
}
后序遍历非递归算法
与中序遍历区别
- 指针非空就进栈,先访问左子树,右子树若没被访问,则标识为0
- 当指针空掉了,该处理栈里面的了。这里注意与中序遍历的区别,当访问栈的右子树时,是不能出栈的
- 当访问栈的右子树时,标识改为1
- 左右子树访问完(标识改为1),出栈(输出)
- 算法实现(顺序栈)
//定义结构体
typedef struct satck
{
bintree data[100];
int tag[100];//定义一个标识
int top;
}seqstack;
//算法实现
void preorder1(bintreet)
{
seqsatck s;//在已经定义栈的情况下
init(&t);//置空栈
while(t||s.top!=-1)
{
if(t){//当前子树不为空的情况
push(&s,t);//入栈
s.tag[s.top]=0;//栈顶的标识改为0
t=t->lchild;//进入栈顶的左子树
}
else{
if(s.tag[s.top]==1)//右子树访问完成
{
t=pop(&s);//出栈
printf("%c",t->data);
t->NULL;//输出后,指针指向的还是这个结点,就变成了指针非空,但是前面指针非空是优先处理指针,所以主动将指针置为空,让它去栈里再取结点
}
else{//栈顶的标识是0,访问右指针
t=s.data[s.top];//去栈顶元,记录进入的右子树,栈顶地址
s.tag[s.top]=1;
t=t->rchild;
}
}
}
}
后序遍历考题1
编写程序,输出二叉树中某结点所有祖先接结点
后序遍历演示中,我们发现E的祖先是BA,B的祖先是A。这题考点就是后序遍历
后序遍历考题2
求二叉树中某结点的层次
和上题一样,最先出栈的祖先是离得最近的层次
后序遍历考题3
给定两个结点,找离他们最近的公共结点,例如E和F最近的公共结点就是A
这是是考题1的基础上的扩展,我们把将两者的祖先结点进行比较,找最靠前的一个公共点即可
二叉树递归算法举例
例1
左边找完右边找
例2
如果找的是叶子,需要左右子树都为空,才是叶子
例3
- 前面t=0是默认不相等
- 先访问根结点,在访问左子树,左子树都相等再访问右子树
例4
比较左右两边高度,最后+1是加上根结点
对这里不太明白高度是不是少条件了
穿线二叉树
中序穿线二叉树
- 增设指针标识
typedef char datatype;//结点属性值的类型
typedef struct node{//二叉树节点类型
datatype data;
int ltag,rlag;//增加左右指针标识
struct node *lchild,*rchild;
}bintnode;//二叉树
typedef bintnode *binthrtree;//指向二叉树结点的指针类型
//bintnode *等价struct node*
/********创建中序穿线二叉树*********/
binthrtree pre=NULL;//我们需要两个指针,这个指针代表遍历这个结点的前驱结点。因为每次递归都要调用,设为全局变量
binthrtree createbinthrtree()
{
char ch;
binthrtree t;
if((ch=getchar())=='#')
t=NULL;
else{
t=(bintnode *)malloc(sizeof(bintnode));
t->data=ch;//建立子树根结点
t->lchild=createbinthrtree();
t->rchild=createbinthrtree();
}
return t;
}
/****对二叉树进行线索化****/
void inthreading(binthrtree *p)
{
if(*p)//只要不是空树
{
inthreading(&((*p)->lchild));//先遍历左子树
(*p)->ltag=((*p)->lchild)?0:1;//判断左子树是否空后把0or1赋给标识
(*p)->rtag=((*p)->rchild)?0:1;
if(pre)//前驱结点存在
{
if(pre->rtag==1) pre->rchild=*p;
if((*p)->ltag==1) (*p)->lchild=pre;
}
inthreading(&((*p)->rchild));//再遍历右子树
}
}
/****遍历穿线二叉树****/
好难理解等理解了再来写
树,森林与二叉树的相互转换
- 将兄弟连线转换
- 树到二叉树的转换方法是:
树中长子关系变为左儿子关系,兄弟关系变为右儿子关系
森林到二叉树的转换是将各棵树根看成兄弟关系
课上练习
C
注意不是问总结点,因此不要-1
总结点数
- 既然是叶子结点,则n0=46
- 根据
n0=n2+1
,n2=45 - 由前面总结,完全二叉树最多有1个度为1的结点
- 最多有:46+45+1=92
层数 - 2^6-1=63,即前面六层是满的
- 还有29结点,所以最多7层
B 2^6-1=63
C
小技巧
-
总结点数为偶数,叶子结点就是
总结点数的一半
-
总结点数为奇数,叶子结点就是
(总结点数+1)/2
这种题结果有很多种,我们应该从选项倒推 -
递归/非递归输出中序遍历首尾点(左右指针都存在的情况,实际要判断)
bintree intorderFirst1(bintree t)
{//非递归算法
bintree p;
p=t;
while(p!=NULL&&p->lchild!=NULL)
p=p->lchild;
return p;
}
bintree intorderFirst1(bintree t)
{//非递归算法
if(t==NULL||t->lchild=NULL)
return t;
else
return intorderFirst1(t->lchild);
return p;
}
- 前序遍历和后序遍历结果相同的二叉树为
只有根结点的二叉树
- 高度为k的二叉树最大结点数为
2^k-1
,最小结点数为k
- n个结点的二叉树,该二叉树所有结点的度数之和是
n-1