《数据结构》:05树与二叉树

树的概念

树是 n(n≥0)个结点的有限集合,n = 0 时,称为空树,这是一种特殊情况。在任意一棵非空树中应满足:

  • 有且仅有一个特定的称为根的结点。
  • 当 n > 1时,其余结点可分为 m(m > 0)个互不相交的有限集合 T1, T2,…, Tm,其中每个集合本身又是一棵树,并且称为根结点的子树。

在这里插入图片描述

**空树:**结点数为 0 的树

非空树的特性:

  • 有且仅有一个根节点
  • 没有后继的结点称为 “叶子结点”(或终端结点)
  • 有后继的结点称为 “分支结点”(或非终端结点)
  • 除了根节点外,任何一个结点都有且仅有一个前驱
  • 每个结点可以有 0 个或多个后继。

树的性质

  1. 树中的结点数等于所有结点的度数加 1。
  2. 度为 m 的树中第 i 层上至多有 mi-1 个结点(i>=1)。
  3. 高度为 h 的 m 叉树至多有 (mh>-1)/(m-1) 个结点。
  4. 具有 n 个结点的 m 叉树的最小高度为 [logm(n(m-1)+1)]。

二叉树的概念

二叉树是另一种树形结构,其特点是每个结点至多只有两棵子树(即二叉树中不存在度大于 2 的结点),并且二叉树的子树有左右之分,其次序不能任意颠倒。

二叉树是nn≥0)个结点的有限集合:

  • 或者为空二叉树,即 n = 0。

  • 或者由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树又分别是一颗二叉树。

二叉树的 5 种基本形态:

在这里插入图片描述

二叉树与度为 2 的有序树的区别:

  1. 度为 2 的树至少有 3 个结点,而二叉树可以为空。
  2. 度为 2 的有序树的孩子的左右次序是相对于另一孩子而言的,若某个结点只有一个孩子,则这个孩子就无须区分其左右次序,而二叉树无论其孩子数是否为 2,均需确定其左右次序,即二叉树的结点次序不是相对于另一结点而言,而是确定的。

特殊的二叉树

满二叉树

一棵高度为 h,且含有 2h - 1 个结点的二叉树

在这里插入图片描述

完全二叉树

当且仅当其每个结点都与高度为 h 的满二叉树中编号为 1~n 的结点一一对应时

在这里插入图片描述

二叉排序树

一棵二叉树或者是空二叉树,或者是具有如下性质的二叉树:

  • 左子树上所有结点的关键字均小于根结点的关键字;

  • 右子树上所有结点的关键字均大于根结点的关键字。

  • 左子树和右子树又各是一棵二叉排序树。

在这里插入图片描述

平衡二叉树

树上任一结点的左子树和右子树的深度之差不超过 1。

在这里插入图片描述

二叉树的性质

  1. 非空二叉树上的叶子结点数等于度为 2 的结点数加 1,即 n0 = n2 + 1。

在这里插入图片描述

  1. 非空二叉树上第 i 层至多有 2i-1 个结点(i≥1),m 叉树第 i 层至多有 mi-1 个结点(i≥1)

  2. 高度为 h 的二叉树至多有 2h+1 个结点(h>=1),高度为 h 的 m 叉树至多有 mh-1/(m-1) 个结点(h>=1)。

在这里插入图片描述

  1. 具有 n 个(n > 0)结点的完全二叉树的高度h为 [log2(n + 1)] 或 [log2n] + 1。

  2. 对于完全二叉树,可以由的结点数 n 推出度为 0、1 和 2 的结点个数为 n0、n1 和 n2,完全二叉树最多只有一个度为 1 的结点,即 n1=0 或 1。

在这里插入图片描述

二叉树的存储结构

顺序存储结构

二义树的顺序存储是指用一组地址连续的存储单元依次自上而下,自左至右存储完全二叉树上的结点元素,即将完全二叉树上编号为i的结点元素存储在一维数组下标为 i-1 的分量中。

依据二叉树的性质,完全二叉树和满二叉树采用顺序存储比较合适,树中结点的序号可以唯一地反映结点之间的逻辑关系,这样既能最大可能地节省存储空间,又能利用数组元素的下标值确定结点在二叉树中的位置,以及结点之间的关系。

但对于一般的二叉树,为了让数组下标能反映二叉树中结点之间的逻辑关系,只能添加一些并不存在的空结点,让其每个结点与完全二叉树上的结点相对照,再存储到一维数组的相应分量中。然而,在最坏情况下,一个高度为h且只有h个结点的单支树却需要占据近 2h-1 个存储单元。如图所示,其中0表示并不存在的空结点。

在这里插入图片描述

注意:这种存储结构建议从数组下标 1 开始存储树中的结点。

链式存储结构

由于顺序存储的空间利用率较低,因此二叉树一般都采用链式存储结构,用链表结点来存储二叉树中的每个结点。在二叉树中,结点结构通常包括若干数据域和若干指针域,二叉链表至少包含 3 个域:数据域 data、左指针域 lchild 和 右指针域 rchild,如图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tkQLX1Zm-1630162135904)(/static/images/20210828/11.png)]

在这里插入图片描述

二叉树的遍历

二叉树的遍历是指按某条搜索路径访问树中每个结点,使得每个结点均被访问一次,而且仅被访问一次。由于二叉树是一种非线性结构,每个结点都可能有两棵子树,因而需要寻找一种规律,以便使二叉树上的结点能排列在一个线性队列上,进而便于遍历。

由二叉树的递归定义可知,遍历一棵二叉树便要决定对根结点 N、左子树 L 和右子树 R 的访问顺序。按照先遍历左子树再遍历右子树的原则,常见的遍历次序有先序(NLR)、**中序(LNR)后序(LRN)**三种遍历算法,其中 “序” 指的是根结点在何时被访问。

在这里插入图片描述

先序遍历

先序遍历(PreOrder)的操作过程如下:

若二叉树为空,则什么也不做;否则,

  1. 访问根结点;
  2. 先序遍历左子树;
  3. 先序遍历右子树。

Go 对应的递归算法如下:

type BiTree struct {
	data   int     // 数据域
	lchild *BiTree // 左孩子指针
	rchild *BiTree // 右孩子指针
}

func PreOrder(T *BiTree) {
	if reflect.DeepEqual(T, BiTree{}) {
		fmt.Println(T)     // 访问根节点
		PreOrder(T.lchild) // 递归遍历左子树
		PreOrder(T.rchild) // 递归遍历右子树
	}
}

中序遍历

中序遍历(InOrder)的操作过程如下:

若二叉树为空,则什么也不做;否则,

  1. 中序遍历左子树;
  2. 访问根结点;
  3. 中序遍历右子树。

Go 对应的递归算法如下:

type BiTree struct {
	data   int     // 数据域
	lchild *BiTree // 左孩子指针
	rchild *BiTree // 右孩子指针
}

func InOrder(T *BiTree) {
	if reflect.DeepEqual(T, BiTree{}) {
		InOrder(T.lchild) // 递归遍历左子树
		fmt.Println(T)    // 访问根节点
		InOrder(T.rchild) // 递归遍历右子树
	}
}

后续遍历

后序遍历(PostOrder)的操作过程如下:

若二叉树为空,则什么也不做;否则,

  1. 后序遍历左子树;
  2. 后序遍历右子树;
  3. 访问根结点。

Go 对应的递归算法如下:

type BiTree struct {
	data   int     // 数据域
	lchild *BiTree // 左孩子指针
	rchild *BiTree // 右孩子指针
}

func PostOrder(T *BiTree) {
	if reflect.DeepEqual(T, BiTree{}) {
		PostOrder(T.lchild) // 递归遍历左子树
		PostOrder(T.rchild) // 递归遍历右子树
		fmt.Println(T)      // 访问根节点
	}
}

三种遍历算法中,递归遍历左、右子树的顺序都是固定的,只是访问根结点的顺序不同。不管采用哪种遍历算法,每个结点都访问一次且仅访问一次,故时间复杂度都是 O(n)。在递归遍历中,递归工作栈的栈深恰好为树的深度,所以在最坏情况下,二叉树是有 n 个结点且深度为 n 的单支树,遍历算法的空间复杂度为 O(n)。

二叉树的层次遍历

算法思想:

  1. 初始化一个辅助队列
  2. 根结点入队
  3. 若队列非空,则队头结点出队,访问该结点,并将其左、右孩子插入队尾(如果有的话)
  4. 重复 3 直至队列为空

在这里插入图片描述

伪代码实现:

在这里插入图片描述

由遍历序列构造二叉树

若只给出一棵二叉树的 前/中/后/层 序遍历序列中的一种,不能唯一确定一棵二叉树。

前序+中序遍历序列

前序遍历:结点、前序遍历子树、前序遍历子树

中序遍历:中序遍历子树、结点、中序遍历子树

在这里插入图片描述

后序+中序遍历序列

后序遍历:前序遍历子树、前序遍历子树、结点

中序遍历:中序遍历子树、结点、中序遍历子树

在这里插入图片描述

层序+中序遍历序列

在这里插入图片描述

注意:前序、后序、层序序列的两两组合无法唯一确定一颗二叉树。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值