《数据结构》期末提纲之Huffman树

本文介绍了Huffman树的基本概念,包括名词解释、树的构造、编码原理以及实现方法。通过实例展示了Huffman编码如何减少数据传输的位数,提供了一种优化的编码策略。同时,文章给出了Huffman树的构建过程和编码实现的基本思路,以及针对一组数据的处理方法。

1.名词解释

  • 路径与权
    从起始节点到目标节点所经过的分支序列为路径,所经分支数目为路径长,若给节点赋值,则称此值为权。
  • 节点的带权路径长
    节点的带全路径唱等于该节点的权与根节点到该节点的路径长之积。
  • 树的带权路径长(WPL)
    树的带权路径长为所有叶子节点的带权路径长之和。
  • 图示(今天带了数位板就直接用笔画了(懒))
    名词示例

2.Huffman树简介

给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。(百度百科

3.Huffman树的构造

分为3步:
1)将每个节点视作一棵树,全部节点组成一个森林。
2)选取所有树中权值最小的两棵树,分别作为一个新节点的左子树与右子树,构成一棵新树,该树的权值为左右两棵子树权值之和。
3)重复步骤二直到仅剩一棵树,该树为所求Huffman树。
如图所示:
Huffman树的构造

4.Huffman编码

比如说要传输一个由ABCDEF组成字符串,每个字母采用0与1编码,传统编码方式需要用到三位。假设要传输这样一个字符串:ABBBBCCDDDDDDDDEEEEEFFFFFFF,即六个字母出现次数分别为1、4、2、8、5、7时,按照传统编码方式需要传输81位。然而,并不是每个字母都要用到三位编码,而且传输位数越多成本越高。所以,需要用最少的位数传输这样的字符串,就要为每个字母重新编码。这种编码就叫做Huffman编码。
Huffman编码分为三部。首先按权值构造Huffman树,然后将该树所有节点与左孩子连接路径标0,与右孩子的标1,根到叶子节点的路径为叶子节点的Huffman编码。如图所示:(还是上面的例子)
Huffman编码
对字符进行Huffman编码后,传输上述字符串仅用64位,明显少于传统编码的81位。

5.Huffman树及编码的实现

  • 基本思路
    首先将各个节点按权值置于优先队列中,数值越小优先级越高。然后开始进行树的构建,每次出队两个节点,将这连个节点与一个新节点构成新树入队,重复以上操作直到队列中只有一个树,该树位Huffman树。将该树出队后,进行BFS并依次记录遍历路径,向左遍历为0,向右遍历为1,将路径存入叶子节点,输出叶子节点与路径,即Huffman编码。

  • 设计问题
    处理一组数据,第一行为一个整数n,代表有n个数据需要处理,第二行到第n+1行每行两个数据,分别为一个字符与该字符在某个序列中出现次数。输出每个字符及其所对应的Huffman编码。

  • 代码实现

#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#include<cmath>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define M 1000000007
using namespace std;
typedef long long LL;
const int maxn = 1e5+6;

char s[maxn];

struct node //节点结构体
{
    char data; //所存字符
    int weight; //所占权值
    queue<int> code; //Huffman编码
    node* l_child; //左孩子
    node* r_child; //右孩子
};

bool cmp(node* x,node* y)
{
    return x->weight > y->weight; //重定义比较
}

vector<node*> a; //充当优先队列

int main()
{
    int n; cin >> n;
    for(int i = 1;i <= n;++i) //输入每个节点信息并且进入数组
    {
        char c;
        int w;
        cin >> c >> w;
        node* tmp = new node;
        tmp->data = c;
        tmp->weight = w;
        tmp->l_child = NULL;
        tmp->r_child = NULL;
        a.push_back(tmp);
        sort(a.begin(),a.end(),cmp);
    }
    //以下为Huffman树构造
    while(a.size() > 1)
    {
        node *tmp1,*tmp2,*tmp;
        tmp = new node;
        tmp1 = a.back(); a.pop_back(); //每次弹出两个权值最小的节点
        tmp2 = a.back(); a.pop_back();
        tmp->l_child = tmp1; //构造新的树
        tmp->r_child = tmp2;
        tmp->weight = tmp1->weight + tmp2->weight;
        a.push_back(tmp); //将新的树存入数组并排序
        sort(a.begin(),a.end(),cmp);
    }
    node* huffman = a.back(); a.pop_back(); //弹出仅剩的一个树,即Huffman树
    //以下为对Huffman树的广度优先遍历
    queue<node*> q2; //BFS队列
    q2.push(huffman);
    while(!q2.empty())
    {
        node* now = q2.front(); q2.pop();
        if(now->l_child == NULL && now->r_child == NULL) //当前节点为叶子节点时
        {
            cout << now->data << ": "; //输出字符
            while(!now->code.empty()) //输出Huffman编码
            {
                cout << now->code.front();
                now->code.pop();
            }
            cout << endl;
        }
        if(now->l_child != NULL) //存在左孩子时
        {
            now->l_child->code = now->code; //左孩子节点Huffman编码为当前节点加一个0
            now->l_child->code.push(0);
            q2.push(now->l_child);
        }
        if(now->r_child != NULL)
        {
            now->r_child->code = now->code; //右孩子节点Huffman编码为当前节点加一个1
            now->r_child->code.push(1);
            q2.push(now->r_child);
        }
    }

    return 0;
}
  • 运行结果
    运行结果
  • 说明
    每个节点的Huffman编码不唯一,但是编码长度唯一,Huffman树的WPL唯一。

相关链接:
二叉树:传送门
BFS:传送门(未写)

总提纲:《数据结构》期末提纲小结

解释一下variable is_same_v<make_huffman_tree_t<_test_sequence>, meta_list<_huffman_tree_node<size_t(5), 0, 7, 0>, _huffman_tree_node<size_t(6), 1, 7, 1>, _huffman_tree_node<size_t(8), 2, 9, 0>, _huffman_tree_node<size_t(4), 3, 6, 1>, _huffman_tree_node<size_t(7), 4, 8, 0>, _huffman_tree_node<size_t(3), 5, 6, 0>, _huffman_tree_node<size_t(7), 6, 8, 1>, _huffman_tree_node<size_t(11), 7, 9, 1>, _huffman_tree_node<size_t(14), 8, 10, 0>, _huffman_tree_node<size_t(19), 9, 10, 1>, _huffman_tree_node<size_t(33), 10, -1, -1>>> provided by <type_traits> Type: const bool Value = true // In namespace std constexpr bool is_same_v = __is_same(meta::meta_list<meta::_huffman_tree_node<5UL, 0, 7, 0>, meta::_huffman_tree_node<6UL, 1, 7, 1>, meta::_huffman_tree_node<8UL, 2, 9, 0>, meta::_huffman_tree_node<4UL, 3, 6, 1>, meta::_huffman_tree_node<7UL, 4, 8, 0>, meta::_huffman_tree_node<3UL, 5, 6, 0>, meta::_huffman_tree_node<7UL, 6, 8, 1>, meta::_huffman_tree_node<11UL, 7, 9, 1>, meta::_huffman_tree_node<14UL, 8, 10, 0>, meta::_huffman_tree_node<19UL, 9, 10, 1>, meta::_huffman_tree_node<33UL, 10, -1, -1>>, meta::meta_list<meta::_huffman_tree_node<5UL, 0, 7, 0>, meta::_huffman_tree_node<6UL, 1, 7, 1>, meta::_huffman_tree_node<8UL, 2, 9, 0>, meta::_huffman_tree_node<4UL, 3, 6, 1>, meta::_huffman_tree_node<7UL, 4, 8, 0>, meta::_huffman_tree_node<3UL, 5, 6, 0>, meta::_huffman_tree_node<7UL, 6, 8, 1>, meta::_huffman_tree_node<11UL, 7, 9, 1>, meta::_huffman_tree_node<14UL, 8, 10, 0>, meta::_huffman_tree_node<19UL, 9, 10, 1>, meta::_huffman_tree_node<33UL, 10, -1, -1>>) template<class _Tp, class _Up> constexpr inline bool std::is_same_v = false
最新发布
12-06
<think>我们正在解释的代码片段是:`variable is_same_v<make_huffman_tree_t<_test_sequence>, meta_list<...>>` 这个代码片段出现在C++模板元编程中,用于类型比较。下面我们逐步分解: 1. **`is_same_v`的作用**: - `std::is_same`是C++标准库中的一个类型特性(在`<type_traits>`头文件中),用于检查两个类型是否完全相同。 - `is_same_v`是C++17中引入的模板变量,它是`std::is_same<T, U>::value`的简写。因此,`is_same_v<T, U>`等价于`std::is_same<T, U>::value`,是一个布尔值(`true`或`false`)。 2. **代码中的类型比较**: - 在这个代码片段中,我们比较两个类型: - `make_huffman_tree_t<_test_sequence>`:这看起来是一个模板别名(使用`_test_sequence`作为参数)的计算结果类型。 - `meta_list<...>`:这里`...`代表一些具体的类型参数,比如`meta_list<node<...>, node<...>, ...>`等,表示一个类型列表。 3. **整体含义**: - 这行代码的作用是检查通过`make_huffman_tree_t`模板(传入`_test_sequence`)生成的类型是否等于预期的`meta_list<...>`类型。 - 如果相等,则`is_same_v`的结果为`true`,否则为`false`。 4. **工作原理**: - 在编译时,编译器会实例化`make_huffman_tree_t<_test_sequence>`,即用`_test_sequence`作为参数调用`make_huffman_tree`的模板元函数,并获取其`type`成员(因为`_t`后缀通常表示获取内部`type`,即`make_huffman_tree_t`可能是`typename make_huffman_tree<...>::type`的别名)。 - 同时,右侧的`meta_list<...>`是一个具体的类型,由一系列节点(如`node`)组成。 - 然后,`is_same_v`会比较这两个类型是否完全相同(包括所有模板参数)。 5. **应用场景**: - 这通常用于静态断言(static_assert)或SFINAE中,以确保元编程的结果符合预期。例如,在单元测试中验证霍夫曼构建算法的正确性。 6. **示例**: ```cpp static_assert(is_same_v<make_huffman_tree_t<test_sequence>, meta_list<node<...>, node<...>, ...>>, "Huffman tree type mismatch"); ``` 如果类型不匹配,编译时将报错。 7. **注意事项**: - 类型比较是严格的,包括模板参数的顺序和数量都必须完全一致。 - 在模板元编程中,这样的检查常用于确保编译期计算的正确性。 因此,这段代码的核心目的是在编译期验证霍夫曼生成算法的输出类型是否与预期类型一致。 §§相关问题§§ 1. C++中`std::is_same`和`std::is_same_v`有什么区别? 2. 在模板元编程中,如何定义和使用类型别名(如`make_huffman_tree_t`)? 3. 如何利用`static_assert`进行编译期断言? 4. 霍夫曼构建算法在编译期实现的一般步骤是什么? </think> ### 解释 `is_same_v<make_huffman_tree_t<_test_sequence>, meta_list<...>>` 在C++模板元编程中,这个表达式用于**编译期类型检查**,核心是验证两个类型是否完全相同。以下是详细解析: --- #### 1. `is_same_v` 的作用 `std::is_same_v` 是C++17引入的**编译期类型比较工具**: ```cpp template <class T, class U> inline constexpr bool is_same_v = is_same<T, U>::value; ``` - **输入**:两个类型 `T` 和 `U` - **输出**:布尔值 `true`(类型相同)或 `false`(类型不同) - **编译期执行**:结果在编译时确定,不产生运行时开销 - **典型用途**: - 静态断言(`static_assert`) - SFINAE(替换失败非错误)控制模板特化[^2] --- #### 2. 代码中的类型比较逻辑 表达式 `is_same_v<make_huffman_tree_t<_test_sequence>, meta_list<...>>` 包含两个关键部分: | 组件 | 说明 | |------|------| | **`make_huffman_tree_t<_test_sequence>`** | 模板元函数调用结果类型<br> • `make_huffman_tree`:霍夫曼生成器<br> • `_test_sequence`:输入数据(如字符频率序列)<br> • `_t` 后缀:C++14风格的类型别名(等价于 `typename make_huffman_tree<...>::type`) | | **`meta_list<...>`** | 预期的结果类型<br> • `meta_list`:类型容器(类似 `std::tuple`)<br> • `...`:具体的类型参数(如 `node<'A', 5>, node<'B', 3>`) | **比较逻辑**: 验证 `make_huffman_tree` 对 `_test_sequence` 的处理结果是否精确匹配预期的 `meta_list<...>` 结构。 --- #### 3. 工作原理示意图 ```mermaid graph LR A[_test_sequence] --> B[make_huffman_tree] B --> C[生成类型 T] D[预期类型 U = meta_list<...>] --> E[is_same_v<T, U>] E -->|true| F[编译通过] E -->|false| G[编译错误] ``` --- #### 4. 实际应用场景 ```cpp // 验证霍夫曼生成是否正确 static_assert( is_same_v< make_huffman_tree_t<test_sequence>, meta_list<node<'A', 5>, node<'B', 3>, node<'C', 1>> >, "Huffman tree generation failed!" ); ``` - **编译时验证**:若生成的与预期结构不匹配,触发编译错误 - **元编程测试**:常用于模板库的单元测试(如验证算法正确性) --- #### 5. 关键特性总结 | 特性 | 说明 | |------|------| | **零运行时开销** | 所有计算在编译期完成 | | **严格类型匹配** | 包括模板参数顺序和数量 | | **错误早发现** | 编译期报错避免运行时问题 | | **依赖类型推导** | 要求 `make_huffman_tree_t` 能推导出具体类型 | > 这种模式在Boost.Hana、C++20 Ranges等库中广泛用于编译期逻辑验证[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值