树and森林
⭐兄弟孩子表示法
需要知道转化,例题如下:
-
找特殊法
-
找准叶子节点法。
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);
}
建立
-
给了链式存储,建立顺序存储
-
给了顺式存储,建立链式存储
树的应用
二叉排序树
二叉排序树:左<根<右。
增、查
增和查可以看作是同样的类型。
查找结点时,需要找到一个合适的位置,可以通过递归将这个空的位置找出来。
增:
// 王道书上有一个更简单的,但是这个看起来似乎也行
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;
}
}
}
删
删除结点,分三种情况:
- 叶子结点——直接删
- 只有一个子树——直接孩子结点顶替它
- 有两个子树——找直接前驱或者后继。
- ⭐删了再增, 可能会改变BST树形状
ASL的计算
很简单,算准就行
平衡二叉树(AVL)
平衡因子:左子树与右子树的高度差。
⭐调整对象:最小不平衡树:以插入路径上,离插入结点最近的平衡因子的绝对值大于1的结点作为根的子树。
选择题会考的
n k = n k − 1 + n k − 2 + 1 n_k=n_{k-1} + n_{k-2} +1 nk=nk−1+nk−2+1
k k k层AVL的最少结点情形:其实就是每个非叶子结点,都是平衡因子为1
【所以这两个是等价的】
插入
四种调整:
LL(右单旋转)
RR(左单旋转)
LR(先左后右双旋转)左子树的右子树的插入导致的失衡。
RL(先右后左双旋转)右子树的左子树的插入导致的失衡。
这里非常推荐跟着例题,去体会怎么掰树枝的过程
查找
O ( n log n ) O(n\log n) O(nlogn)
哈夫曼树及其编码
WPL:从根出发,到任意一个叶子结点所经历的路径长度(也就是经过的边数)乘以该结点上权值的乘积。
‼️ 注意,是叶子结点,一定一定要注意!!!
哈夫曼树:WPL最小的N叉树。
哈夫曼树的构造
- 森林中选两棵最小树
- 合二为一,并重新加入到森林
- 循环1,至ok
哈夫曼树结点数: 2 n − 1 2n-1 2n−1
前缀编码
没有一个编码是另一个编码的前缀。
考虑问题:哈夫曼的长度不超过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=(k−1)nk+1
则需要有: ( n 0 − 1 ) % ( k − 1 ) = 0 (n_0-1)\%(k-1)=0 (n0−1)%(k−1)=0;
当 ( n 0 − 1 ) % ( k − 1 ) = u ≠ 0 (n_0-1)\%(k-1)=u\ne0 (n0−1)%(k−1)=u=0:当不满足于此,则在 n k n_k nk个内节点的基础上,增加一个内节点;这个内节点,本来是叶结节的位置,然后因为来了 u u u个叶子,所以要这个老叶子拆下来,然后添加一个内节点,再添上 k − u − 1 k-u-1 k−u−1个虚段。【算了,记结论吧】