让你迅速了解并掌握二叉树的秘籍

二叉树

谈到二叉树的时候,大家会觉得,二叉树好难啊,掌握不了,其实是因为大家没有理解二叉树的精髓,下面让我带你们走一遍。

二叉树是建立在树的基础上的,因此我将介绍一下树中的一些名词,不然后面会看着很懵逼的。

重点需要掌握的名词:

节点的度:一个节点含有的子树的个数称为该节点的度
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
兄弟节点:具有相同父节点的节点互称为兄弟节点;
树的高度或深度:树中节点的最大层次

在这里插入图片描述
如图所示:就是一颗完整的树。
一棵树的子节点之间不能够交叉。例如P和Q之间。
对上述名词举个例子:D的A的子节点,同时也是H的父节点。
树的深度:从A的一层开始算,如果A的层数是0,那么深度为3;

那我们正式开始介绍二叉树了,仔细看咯!
一、二叉树概念及结构
1.1概念
一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。

二叉树的特点:
1.每个结点最多有两棵子树,即二叉树不存在度大于2的结点。
2.二叉树的子树有左右之分,其子树的次序不能颠倒。
看着概念感觉到很枯燥,不如直接上图!
在这里插入图片描述
简单来讲,就是一个父节点只能有两个子节点,左边的子节点称为左子树,右边的节点称为右子树.

1.2 特殊的二叉树:
1.满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。

2.完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
oh my god! 看着这概念好头疼,不如来跟我看看图!

这是一颗满二叉树
在这里插入图片描述
这是一颗完全二叉树

总结一下就是:满二叉树是每一层的节点都是满的
完全二叉树是只有最后一层的节点可以不满,其余层都要满。
因此满二叉树是一种特殊的完全二叉树

二叉树的性质:
1.若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1) 个结点.
2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h- 1
3. 对任何一棵二叉树, 如果度为0其叶结点个数为 n0, 度为2的分支结点个数为 n2,则有n0=n2+1
4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=Log2(n+1). (ps:Log2(n+1)是log以2为底,n+1为对数)
5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:
1.若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
2.若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子.
3.若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子.

加粗部分尽量掌握。前面四条针对选择题,最后一条针对编程题。

二、二叉树的顺序结构及实现
2.1 二叉树的顺序结构
对于一颗完全二叉树,我们常常用堆来进行存储。
需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

堆是什么?
它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中。
堆分为大堆和小堆,其结构如图所示:
在这里插入图片描述在这里插入图片描述
堆的性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。

堆的实现:
堆向下调整算法:
现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

void AdjustDown(Heap* hp,int parent,int n)
{
	int child = parent*2 + 1;
	while (child < n)
	{
	if (child+1 <n && hp->_a[child + 1] > hp->_a[child] )
		{
			child++;
		}
		if (hp->_a[child] > hp->_a[parent] )
		{
			Swap1(&hp->_a[child], &hp->_a[parent]);
			Swap2(&child, &parent);
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}	
}

堆的创建:
下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。

堆的结构就像顺序表一样.
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;

void HeapCreate(Heap* hp, HPDataType* a, int n)
{
	hp->_a=(HPDataType*)malloc(sizeof(HPDataType) * n);
	hp->_capacity = n;
	hp->_size = 0;
	for (int i = 0; i < n; i++)
	{
		hp->_a[hp->_size] = a[hp->_size];
		hp->_size++;
	}
	while (n > 0)
	{
		AdjustDown(hp, n-1,hp->_size);	
		n--;
	}
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这样的话我们的大堆就建成了!。

有堆向下调整,自然有堆向上调整啦。

void AdjustUp(Heap* hp, int child)
{
	while (child > 0)
	{
		int parent = (child - 1) / 2;
		if (hp->_a[parent] > hp->_a[child])
		{
			Swap1(&hp->_a[parent],&hp->_a[child]);
			Swap2(&parent,&child);
		}
		else
		{
			break;
		}
	}
}

堆的插入
实现原理:在数组的最后插入一个值,然后向上调整形成堆.

堆的删除
将最后一个数与堆顶的数进行交换,然后删除数组最后一个元素。
再进行向下调整算法即可。

堆排序(十分重要的排序)
实现原理:
堆排序是指通过将堆顶的元素与数组的最后一个元素进行交换,再进行向下调整得到.如图所示
在这里插入图片描述
此时又是一个小堆,逐次进行,最终得到:在这里插入图片描述
要排升序,是建小堆还是大堆?
答案是:大堆。(不理解的话就看一下上面的过程噢~希望能够帮助到你们)
同理,排降序是建小堆。

三、二叉树链式结构的遍历

所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问 题。 遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。

前序/中序/后序的递归结构遍历:是根据访问结点操作发生位置命名。
前序遍历:根节点 左子树 右子树
中序遍历:左子树 根节点 右子树
后序遍历:左子树 右子树 根节点
在这里插入图片描述
前序遍历:就是 3-4-16-17–5-20;
中序遍历:16-4-17-3-20-5
后序遍历:16-17-4-20-5-3

如何简单地理解各种遍历呢?
仔细看!
对于所有的遍历来说都是一样的,就是当你到了一个节点的时候,你根据你所执行的遍历方式来观察左右子树。
例如中序遍历:
到结点3的时候,3是否有左子树或右子树?
答案是有的,3的左子树是4,右子树是5.
中序遍历的顺序是 左子树 根 右子树
因此我们需要先去看结点4。再看结点4是否具有左子树或右子树?
答案是有的,4的左子树是16,右子树是17
因此我们要先遍历16.再观察16是否具有左子树或右子树?
答案是没有,因此我们直接输出16,再回到它的父节点4,再去到4的右子树17.此时4已经遍历结束了。
再回到4的父结点3,再去3的右子树。
这个方法适用于任何遍历的方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值