[数据结构]二叉树

二叉树的概念和性质

二叉树与一般树型结构主要区别
在这里插入图片描述

二叉树五种基本形态

在这里插入图片描述

概念

在这里插入图片描述

性质

  • 一棵非空二叉树的第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
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值