二叉树学习笔记

二叉树学习笔记

一.什么叫做二叉树及其特殊形态

二叉树是每个结点最多有两个子树的树结构。
其子树通常被称作“左子树”和“右子树”。

二叉树的五种基本形态:
①.空集②.只有一个结点③.只有左子树④.只有右子树⑤.左右子树都存在

两种特殊形态的二叉树为:
1.满二叉树
一根深度为k且有2k-1个结点的二叉树称为满二叉树;
也就是说二叉树上只有最后一层无左右子树之外,其余结点的左右子树都存在,形如下图所示。
满二叉树的图

2.完全二叉树
深度为k的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,就是完全二叉树;
也就是说,完全二叉树是除去最后一层之外,其余结点构成满二叉树,最后一层的结点符合左侧连续存在的特点。(注意:满二叉树不是完全二叉树)
这些都是完全二叉树:
在这里插入图片描述
在这里插入图片描述
这些都不是完全二叉树:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二.二叉树常用的性质

1.在二叉树的第i层上最多有2i-1个结点
每一个结点可以拓展出来两个子结点,所以每一层最多是2的指数级增长。

2.深度为k的二叉树至多有2k-1个结点
因为最初始的根节点只有一个,所以所有结点加起来-1最多也就是2k-1个。

3.对于任何一颗二叉树而言,度为0的结点(也就是叶子结点),比度为2的结点多一个:n0 = n2 + 1。

4. B = 2 * n2 + 1 * n1(B为边数之和)
度为2的边数有两条,度为1的边数有一条,每一条边对应着下端的一个结点,只有最上端的结点①没有边数对应,所以:
n = B + 1

所以我们就可以得到: n = n0 + n1 + n2 = 2 * n2 + n1 + 1;
这个也能推导出3的性质,同时也能计算结点数。

5.具有n个结点的完全二叉树的深度为log2n +1(log2n向下取整)
这个可以算二叉树的深度为多少,也可以判断两个结点是否在同一层上(两个结点的向下取整是否相等)。

三.二叉树的存储结构

1.顺序存储

数组来依次从上至下,从左至右存储完全二叉树上的结点元素(注意:顺序存储适用于完全二叉树,因为在非完全二叉树上会存在浪费空间的情况

顺序存储中最重要的就是:

  1. 根结点从数组下标为1开始的时候:当父结点下标为i时,左孩子结点下标为2i,右孩子结点下标为2i+1;
  2. 根结点从数组下标为0开始的时候,当父结点下标为i时,左孩子结点下标为2i+1,右孩子结点下标为2i+2;

二叉树的顺序存储的优点是代码好写,便于查找;缺点是不易实现增加、删除的操作;

2.链式存储结构

链表的方式来存储,每个结点至少由数据域+左指针+右指针三个数据成员组成(当然还可以加上一个数据成员来指向父节点,这种表示称为三叉链表);

两种链表:

三个数据成员左指针数据域右指针
四个数据成员左指针数据域右指针父结点指针

加上一个父结点指针是为了能够更快的找到其结点的父结点。

当有n个结点时,那么
总共有2n个链域,有(n-1)个有指向,即有(n+1)个空链域。

四.二叉树的遍历

1.先序遍历

其遍历过程为:根结点 —> 左子树 —> 右子树;
具体来说就是:
①访问根结点;
②先序遍历左子树;
③先序遍历右子树;

从上述描述我们都能看出,用递归就能很快写出其遍历过程:

//根节点从0开始的顺序存储结构:
int n;
//n这里指结点个数
void PoTravel(int a[],int x)
{
	if(x < n)
	{
		printf("%d ",a[x]);
		potravel(a,2*x + 1);
		potravel(a,2*x + 2);
	}
}

//链式存储结构:
void PoTravel(Bintree BT)
{
	if(BT){
	printf("%d ",BT->data);
	PoTravel(BT->left);
	PoTravel(BT->right);
	}
}
	

2.中序遍历

其遍历过程为:左子树 —> 根结点 —> 右子树;
具体来说就是:
①中序遍历左子树;
②访问根结点;
③中序遍历右子树;

//根节点从0开始的顺序存储结构:
int n;
//n这里指结点个数
void IoTravel(int a[],int x)
{
	if(x < n)
	{
		Iotravel(a,2*x + 1);
		printf("%d ",a[x]);
		Iotravel(a,2*x + 2);
	}
}

//链式存储结构:
void IoTravel(Bintree BT)
{
	if(BT){
	IoTravel(BT->left);
	printf("%d ",BT->data);
	IoTravel(BT->right);
	}
}
	

3.后序遍历

其遍历过程为:左子树 —> 右子树 —> 根结点;
具体来说就是:
①后序遍历左子树;
②后序遍历右子树;
③访问根结点;

//根节点从0开始的顺序存储结构:
int n;
//n这里指结点个数
void IoTravel(int a[],int x)
{
	if(x < n)
	{
		Iotravel(a,2*x + 1);
		Iotravel(a,2*x + 2);
		printf("%d ",a[x]);
	}
}

//链式存储结构:
void IoTravel(Bintree BT)
{
	if(BT){
	IoTravel(BT->left);
	IoTravel(BT->right);
	printf("%d ",BT->data);
	}
}
	

4.层序遍历

从上到下,从左向右按照层次进行遍历;

5.注意

若已知先序序列(或后序序列)和中序序列,能够构造出对应的唯一的二叉树

①根结点必在后序序列的尾部,根结点必在先序序列的最前面
②根结点必在中序序列的中间,而且其左边全部都是左子树的子孙结点,右边全部都是右子树的子孙结点
③由①与②可以递归确定左右子树与根结点,最后确定其二叉树

例如:
已知一个二叉树的中序序列和后序序列,如何来求其先序序列呢?

首先在后序序列中,根结点在最后一个;在中序序列中,根节点在左子树和右子树的中间,所以我们就能区分出来左子树和右子树……(元素不重复的二叉树才行)

①取出后序序列的最后一个元素,然后放到中序序列中进行比对,找到根结点的位置;
②记录下左子树的长度m和右子树的长度n,然后回到后序序列中找到根结点,区分开左子树与右子树(前m个为左子树);
③按照先序序列的输出顺序,先输出根结点,然后递归左子树,在递归右子树,重复步骤①②直到没有左子树与右子树

代码如下:

char s[N],ss[N];
//s数组记录的中序序列,ss数组记录的后序序列
void search(int l1,int r1,int l2,int r2)
{
	printf("%c",ss[r2]);
	//输出根结点
	int m;
	//m用来查找中序序列的根结点
	for(int i = l1; i <= r1; i++)
	{
		if(ss[r2] == s[i])
		{//将每一个结点和后序序列最后一个结点比较
		//查找根结点在中序序列的位置
			m = i;
			break;
		}
	}
	int left = m -l1;
	int right = r1 - m;
	//记录左右子树的长度
	if(left >= 1)
		search(l1,m-1,l2,r2-right-1);
	if(right >= 1)
		search(m+1,r1,l2+left,r2-1);
	//如果还有左右子树,继续递归
}

int main()
{
	scanf("%s %s",s,ss);
	int len = strlen(s);
	search(0,len-1,0,len-1);
	return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值