【数据结构】树_复习笔记总结

一、基本定义与性质

1. 基本定义

  • 结点的度: 结点拥有的子树个数
  • 树的度: 树内各结点度的最大值
  • 叶子 / 终端结点: 度为0的结点
  • 双亲和孩子: 结点的子树称为该结点的孩子,该结点叫做孩子的双亲
  • 兄弟: 同一个双亲的孩子互称兄弟
  • 祖先: 从根到该结点路径上的所有结点
  • 子孙: 以某结点为根的子树中的任意结点
  • 堂兄弟: 双亲在同一层的结点
  • 层次: 根为第一层,孩子结点的层次等于双亲结点的层次加1
  • 树的深度: 树中结点的最大层次数
  • 森林: 互不相交的树的集合

二叉树的五种基本形态:二叉树的五种形态

  • 满二叉树: 深度为k,有2k -1个结点
  • 完全二叉树: 深度为k,有n个结点,每个结点编号都与深度为k的满二叉树中编号相同(说人话就是只有最后一层可能没放满,且从左到右不留空地放)
  • 需要注意的是:二叉树不是树的特殊形态!二叉树不属于树!

2. 性质

  • 性质1 在二叉树的第 i 层上至多有2 i-1(i >= 1)个结点
  • 性质2 深度为 k 的二叉树至多有2 k - 1(k>=1)个结点
  • 性质3 对任何一颗二叉树T,如果其终端结点数为n0 ,度为2的结点数为n2 ,则n0 = n2 + 1
    • 证明:设n1 为度为1的结点个数,结点总数n = n0 + n1 + n2
      设B为分支总数
      除了根结点之外,每个结点都有一条分支进入,所以 n = B + 1
      除了叶子结点的每个结点都会发出分支,所以 n = 2n2 + n1
      所以有 n = n1 + 2n2 + 1
      得 n0 = n2 + 1
  • 性质4 具有 n 个结点的完全二叉树深度为⌊log2 n⌋ + 1
  • 性质5 如果对一棵有 n 个结点的完全二叉树按层序编号:
    • 如果 i = 1,则结点 i 是二叉树的根
    • 如果 2i > n,则结点 i 无左孩子,否则左孩子是结点 2i
    • 如果 2i + 1 > n,则结点 i 无右孩子,否则右孩子是结点 2i + 1

二、存储、操作与算法

以下均以二叉树为例

1. 二叉树的存储结构

(1) 顺序存储

#define MAXSIZE 100
typedef ElemType SqBiTree[MAXSIZE];
SqBiTree bt;

这种存储结构仅适用于完全二叉树,因此我们大部分采用链式存储

(2) 链式存储

typedef struct BiTNode
{
	ElemType data;
	struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

2. 遍历二叉树

由于考试范围里说不考非递归,复习时间紧,所以这个坑先放着啦,希望还记得来填上

(1) 先序遍历 - 根左右

A. 递归算法
void InOrderTraverse(BiTree T)
{
	if (T) // 若二叉树非空
	{
		cout << T->data;
		InOrderTraverse(T->lchild);
		InOrderTraverse(T->rchild);
	}
}

(2) 中序遍历 - 左根右

A. 递归算法
void InOrderTraverse(BiTree T)
{
	if (T) // 若二叉树非空
	{
		InOrderTraverse(T->lchild);
		cout << T->data;
		InOrderTraverse(T->rchild);
	}
}

(3) 后序遍历 - 左右根

A. 递归算法
void InOrderTraverse(BiTree T)
{
	if (T) // 若二叉树非空
	{
		InOrderTraverse(T->lchild);
		InOrderTraverse(T->rchild);
		cout << T->data;
	}
}

(4) 根据遍历序列确定二叉树

在二叉树的先序遍历、中序遍历、后序遍历中选两个确定二叉树,这两个中必须有一个是中序遍历
思路很简单啦就是根据先序遍历or后序遍历找到根,再在中序遍历中找到根的位置,在根前面的就是左子树,在根后面的就是右子树,然后返回先序遍历or后序遍历继续找两个子树的根,这样递归下去就能确定整个二叉树啦
还是因为考试原因留个代码的坑啦~

(5) 计算二叉树的深度

思路: 分别递归计算一个结点左右子树的深度,这个结点的深度就是左右结点深度中较大的那个加1

int Depth(BiTree T)
{
	if (T == NULL) return 0;
	else
	{
		m = Depth(T->lchild); // 递归计算左子树深度
		n = Depth(T->rchild); // 递归计算右子树深度
		if (m > n) return (m + 1);
		else return (n + 1);
	}
}

(6) 计算二叉树结点的个数

思路: 空树返回0,不是空树就返回左子树个数加右子树个数加1

int NodeCount(BiTree T)
{
	if (T == NULL) return 0;
	else return NodeCount(T->lchild) + NodeCount(T->rchild) + 1;
}

3. 树和森林

(1) 树的存储结构

双亲表示法

每个结点存自身数值和双亲,求双亲和根很容易,但是求孩子很难

孩子表示法

孩子数目多时,像单链表那样把孩子存起来

孩子兄弟表示法

每个结点存自身数值、第一个孩子结点和下一个兄弟结点
在树的存储中应用较为普遍

(2) 树(森林)与二叉树的转换

树转换成二叉树

在这里插入图片描述

  1. 在所有的兄弟结点之间加一条线
    在这里插入图片描述
  2. 树中的每个结点只保留与第一个孩子的连线,删去其他的在这里插入图片描述
  3. 调整整个树的结构,让兄弟转换一下变成右孩子在这里插入图片描述
    bingo~完成!
二叉树转换成树
  1. 将二叉树左上到右下分层排列整齐在这里插入图片描述
  2. 找到每层结点的父结点,加线
    在这里插入图片描述
  3. 删掉兄弟之间的连线
    在这里插入图片描述
    完成~
二叉树转换成森林
  1. 从根节点开始,删去右孩子的连线
  2. 再看被删去的那一部分,继续删去右孩子的连线,直到全部删去为止
  3. 将得到的二叉树转换成树

4. 哈夫曼树(Huffman)

哈夫曼树又叫做最优树,是带权路径长度最短的树。

  • 路径:从一个结点到另一个结点的分支
  • 路径长度:路径上的分支数目
  • 树的路径长度:树根到每一结点的路径长度之和
  • 结点的带权路径长度:结点到树根的路径长度与结点上权值的乘积
  • 树的带权路径长度:树中所有叶子结点的带权路径长度之和,记作WPL
  • 哈夫曼:带权路径长度最小的二叉树

(1) 构造过程

  1. 找到当前所有结点中最小的两个,将这两个结点作为兄弟,连在同一个双亲上,双亲的权值是两个结点权值之和
  2. 把这两个结点从点集中删去,把双亲结点加入点集
  3. 继续寻找目前点集中最小的两个,重复步骤12,直到点集中只剩下一个点

举个栗子

w = (5, 29, 7, 8, 14, 23, 3, 11) 构造哈夫曼树并计算带权路径长度

先在 w 里找到最小的两个点——3,5,然后构造:在这里插入图片描述
现在点集变为 w = (8, 29, 7, 8, 14, 23, 11),找到最小的两个——7, 8,然后构造:在这里插入图片描述
现在点集变为 w = (15, 29, 8, 14, 23, 11),找到最小的两个——8, 11,然后构造:在这里插入图片描述
现在点集变为 w = (15, 29, 17, 14, 23),找到最小的两个——14, 15,然后构造:在这里插入图片描述
现在点集变为 w = (29, 29, 17, 23),找到最小的两个——17, 23,然后构造:在这里插入图片描述
现在点集变为 w = (29, 29, 40),找到最小的两个——29, 29,然后构造:在这里插入图片描述
现在点集变为 w = (58, 40),找到最小的两个——40, 58,然后构造:
在这里插入图片描述
WPL = 8 x 3 + 11 x 3 + 23 x 2 + 29 x 2 + 14 x 3 + 7 x 4 + 3 x 5 + 5 x 5 = 271

(2) 算法实现

(挖坑~)

5. 哈夫曼编码

列出给定字母,字母出现的次数为权值,构造哈夫曼树
然后从根结点开始走,左0右1,得到的字串即为哈夫曼编码

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Texcavator

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值