【408数据结构】树(二)

树and森林

⭐兄弟孩子表示法

需要知道转化,例题如下:
在这里插入图片描述

  1. 找特殊法

  2. 找准叶子节点法。

    F的叶子节点→T中无左孩子

    F无兄弟结点→T中无右孩子

    非终端结点→带来一个T中的空右指针

满二叉树、完全二叉树

基于顺序存储的树

逻辑↓

物理存储↓

在这里插入图片描述

但是,在这里,我们沿用这里的数组一般从1开始的规定,与王道书一致。而非上图数组下标从0开始。

封装

int find_lchild(int i, int n)
{
	if(2 * i > n)
		return -1; // 没有左孩子
	return 2 * i;
}

int find_lchild(int i, int n)
{
	if(2 * i + 1> n)
		return -1; // 没有右孩子
	return 2 * i + 1;
}

int find_pa(int i)
{
	return i / 2;
}

遍历

遍历还是递归。以前序为例。

void pre_visit(int i, int n)
{
	if(i < 1 || i > n)
		return;
	visit(i);
	pre_visit(find_lchild);
	pre_visit(find_rchild);
}

建立

  1. 给了链式存储,建立顺序存储

  2. 给了顺式存储,建立链式存储

树的应用

二叉排序树

二叉排序树:左<根<右。

增、查

增和查可以看作是同样的类型。

查找结点时,需要找到一个合适的位置,可以通过递归将这个空的位置找出来。

增:

// 王道书上有一个更简单的,但是这个看起来似乎也行
void insert(Node *T, int data)
{
	if(data < T->data)
	{
		if(T->lchild)
			insert(T->lchild, data);
		else{
			Node *q = (Node *)malloc(sizeof(Node));
			q->data = data;
			T->lchild = q;
		}
	}
	else if (data > T->data)
	{
		if(T->rchild)
			insert(T->rchild, data);
		else{
			Node *q = (Node *)malloc(sizeof(Node));
			q->data = data;
			T->rchild = q;
		}
	}
}

删除结点,分三种情况:

  1. 叶子结点——直接删
  2. 只有一个子树——直接孩子结点顶替它
  3. 有两个子树——找直接前驱或者后继。
  4. ⭐删了再增, 可能会改变BST树形状

ASL的计算

很简单,算准就行

平衡二叉树(AVL)

平衡因子:左子树与右子树的高度差。

⭐调整对象:最小不平衡树:以插入路径上,离插入结点最近的平衡因子的绝对值大于1的结点作为根的子树。

选择题会考的

n k = n k − 1 + n k − 2 + 1 n_k=n_{k-1} + n_{k-2} +1 nk=nk1+nk2+1

k k k层AVL的最少结点情形:其实就是每个非叶子结点,都是平衡因子为1

【所以这两个是等价的】

插入

四种调整:

LL(右单旋转)

RR(左单旋转)

LR(先左后右双旋转)左子树的右子树的插入导致的失衡。

在这里插入图片描述

RL(先右后左双旋转)右子树的左子树的插入导致的失衡。

在这里插入图片描述

这里非常推荐跟着例题,去体会怎么掰树枝的过程

查找

O ( n log ⁡ n ) O(n\log n) O(nlogn)

哈夫曼树及其编码

WPL:从根出发,到任意一个叶子结点所经历的路径长度(也就是经过的边数)乘以该结点上权值的乘积。

‼️ 注意,是叶子结点,一定一定要注意!!!

哈夫曼树:WPL最小的N叉树。

哈夫曼树的构造

  1. 森林中选两棵最小树
  2. 合二为一,并重新加入到森林
  3. 循环1,至ok

哈夫曼树结点数: 2 n − 1 2n-1 2n1

前缀编码

没有一个编码是另一个编码的前缀。

在这里插入图片描述

考虑问题:哈夫曼的长度不超过4,即哈夫曼树的高为5;这点非常关键。

高为5;则最多是这种情形:

在这里插入图片描述

最佳归并树(N叉哈夫曼树)

当初始结点不足以构成一颗严格的N叉树,需要添加虚段。

推导:

设度为0的点有 n 0 = n n_0=n n0=n个,度为k的点 n k n_k nk个;

则对一个严格的N叉树,有:

【节点数=度+1】

n 0 = ( k − 1 ) n k + 1 n_0= (k-1)n_k+1 n0=(k1)nk+1

则需要有: ( n 0 − 1 ) % ( k − 1 ) = 0 (n_0-1)\%(k-1)=0 (n01)%(k1)=0

( n 0 − 1 ) % ( k − 1 ) = u ≠ 0 (n_0-1)\%(k-1)=u\ne0 (n01)%(k1)=u=0:当不满足于此,则在 n k n_k nk个内节点的基础上,增加一个内节点;这个内节点,本来是叶结节的位置,然后因为来了 u u u个叶子,所以要这个老叶子拆下来,然后添加一个内节点,再添上 k − u − 1 k-u-1 ku1个虚段。【算了,记结论吧】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RBDE79GK-1645887067790)(%E6%A0%91%20d6f46/Untitled%2010.png)]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值