【数据结构】树(堆、哈夫曼树、二叉排序树、AVL树、)(更新中....)


在这里插入图片描述

二叉树

二叉树(binary tree)是一个有限的结点集合,这个集合或者为空,或者由一个根结点和两棵互不相交的称为左子树(left subtree)和右子树(right subtree)的二叉树组成。

二叉树和度为2的树(2次树)是不同的,对于非空树,其差别表现在以下两点:

  • 度为2的树中至少有一个结点的度为2,而二叉树没有这种要求;
  • 度为2的树不区分左、右子树,而二叉树是严格区分左、右子树的

满二叉树

在一棵二叉树中,如果所有分支结点都有左孩子结点和右孩子结点,并且叶子结点都集中在二叉树的最下一层.

完全二叉树

若二叉树中最多只有最下面两层的结点的度数可以小于2,并且最下面一层的叶子结点都依次排列在该层最左边的位置上,则这样的二叉树称为完全二叉树(complete binary tree)

二叉树的性质

n 为节点,d为总的度数

  1. 非空二叉树上的叶子结点数等于双分支结点数加1: n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1
  2. n = n 0 + n 1 + n 2 n = n_0+n_1+n_2 n=n0+n1+n2
  3. 高度为 h的二叉树最多有 2 h − 1 2^h-1 2h1个结点(h ≥ 1)
  4. 非空二叉树的第i层上最多有 2 i − 1 2^{i-1} 2i1个结点(i ≥ 1)
  5. 具有n个(n>0)结点的完全二叉树的高度为 l o g 2 ( n + 1 ) log_2(n+1) log2(n+1) l o g 2 n + 1 log_2{n}+1 log2n+1

二叉树和树的相互转换

树变二叉树

在这里插入图片描述

二叉树变树

在这里插入图片描述

二叉树变森林

在这里插入图片描述


线索二叉树

对于具有n个结点的二叉树,当采用二叉链存储结构时,每个结点有两个指针域,总共有2n个指针域,又由于只有n-1个结点被有效指针域所指向(n个结点中只有根结点没有被有效指针域指向),则共有 2 n − ( n − 1 ) = n + 1 2n-(n-1)=n+1 2n(n1)=n+1个空链域

为了利用这些空链域,我们可以这样:

  • 如果左指针为空,则指向前驱节点
  • 如果右指针为空,则指向后继节点

这样的指向该线性序列中的“前驱结点”和“后继结点”的指针称为线索(thread)。创建线索的过程称为线索化。线索化的二叉树称为线索二叉树(threaded binary-tree)。

由于遍历方式不同,产生的遍历线性序列也不同,会得到相应的线索二叉树。一般有先序线索二叉树、中序线索二叉树和后序线索二叉树。创建线索二叉树的目的是提高该遍历过程的效率。

那么,在线索二叉树中如何区分左指针指向的是左孩子结点还是前驱结点,右指针指向的是右孩子结点还是后继结点呢?

答:增加标志位
在这里插入图片描述

在这里插入图片描述


哈夫曼树

在这里插入图片描述

当二叉树的WPL最小时,该二叉树被称为最优二叉树或哈夫曼树

哈夫曼的构造(贪心)

在这里插入图片描述
此时 W P L = 7 ∗ 1 + 5 ∗ 2 + ( 1 + 3 ) ∗ 3 = 4 + 9 + 16 = 29 WPL = 7*1+5*2+(1+3)*3 = 4 + 9 + 16 = 29 WPL=71+52+(1+3)3=4+9+16=29

哈夫曼编码

在数据通信中,经常需要将传送的文字转换为二进制字符0和1组成的二进制字符串,称这个过程为编码。显然,我们希望电文编码的代码长度最短。哈夫曼树可用于构造使电文编码的代码长度最短的编码方案。

左分支为0,右分支为1

在这里插入图片描述

题目:

【模板】哈夫曼编码_牛客题霸_牛客网

#include <iostream>
#include <queue>
#include <vector>
using namespace std;
using LL = long long;
int main() 
{
   
    int n;
    cin >> n;
    priority_queue<LL, vector<LL>, greater<LL>> heap;
    while(n--)
    {
   
        LL x;
        cin >> x;
        heap.push(x);
    }
    //建树
    LL ret = 0;
    while(heap.size() > 1)
    {
   
        LL t1 = heap.top(); heap.pop();
        LL t2 = heap.top(); heap.pop();
        heap.push(t1+t2);
        ret += t1+t2;
    }
    std::cout << ret << std::endl;
}
// 64 位输出请用 printf("%lld")

堆树的定义如下:

(1)堆树是一颗完全二叉树;

(2)堆树中某个节点的值总是不大于或不小于其孩子节点的值;

(3)堆树中每个节点的子树都是堆树。
当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆。 当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆。如下图所示,左边为最大堆,右边为最小堆。
在这里插入图片描述

建堆

堆的主要应用是堆排序,堆排序的核心操作是建堆。

建堆有2种方式:向上调整建堆向下调整建堆

向上调整建堆

向上调整建堆:将a[0]之后的数据看成一次次插入进去,先插入在尾部,而后一步步向上调整。
在这里插入图片描述
代码:

for(int i = 1; i < n; i++)
{
   
	AdjustUp(i);
}
template<class T, class cmp>
void Heap<T, cmp>::AdjustUp(int child)
{
   
    if(child == 0) return;
    int father = (child-1)/2;
    while(cmp()(_heap[child], _heap[father]))
    {
   
        std::swap(_heap[child], _heap[father]);
        child = father;
        father = (child-1)/2;
    }
}

复杂度分析:
在这里插入图片描述

向下调整建堆

向下调整建堆:从 n/2 处开始调整
在这里插入图片描述

for(int i = n/2; i >= 0; i++)
{
   
	AdjustDown(i);
}
template<class T, class cmp
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值