基于C++的ADT封装——哈夫曼树

哈夫曼编码

在进行信息传输时,我们通常需要将字符串转化为一串由0和1构成编码。

不难想到,我们可以将每一个字符ASCLL码值的二进制编码作为转化后的编码。每个字符对应了八位数字,这就会导致最后传输的数据十分长。

因此,我们需要一种更好的编码方式——哈夫曼编码。与二进制编码不同的是,每个字符的编码是不定长的,这就给予了编码一定的灵活性。在哈夫曼编码过程中,会给每一个字符赋予一个特殊的权值。一般来说,这个权值可以根据字符出现的频次来进行设定,出现频次越高的字符,我们就使其长度越短,这就保证了编码总长度最短。

如,我们对{"a":1,"b":5,"c":3,"d":11,"e":4,"f":17}进行哈夫曼编码时,得到了{100,101,110,00,111,01}这样一组哈夫曼编码(哈夫曼编码并不唯一),我们可以发现,由于其中任何一个字符编码都不会是另一个字符编码的前缀,因此每一个字符编码都可以被唯一地识别,这也确保了哈夫曼编码的准确性。


哈夫曼树

树的带权路径长度之和(WPL)为其所有叶结点的带权路径长度总和,则对于权值为W,所在层数为L(根节点层数认为是0)的每一个i号叶结点,树的WPL值为:

\sum Wi*Li

而哈夫曼树则是由同一组叶结点创建的所有树中,WPL值最小的二叉树。

创建哈夫曼树的过程中,我们每一次需要选择两个权值最小的结点,并将他们组成一个新的父结点,父结点权值为子结点权值之和。同时,通过这种方式,我们为这两个子结点分别赋予了相应的编码,编码规则为左0右1或左1右0。这样,当我们将整棵哈夫曼树建立起来之后,从根结点到相应叶结点经过的路径则对应了其哈夫曼编码。


堆优化哈夫曼树的代码实现

不难发现,哈夫曼的创建过程需要不断选取当前权值最小的结点,因此,我们可以使用小根堆来优化这个过程。以下则是相应部分的代码实现:

priority_queue.h

#ifndef _priority_queue_H
#define _priority_queue_H
#define lc(x) x << 1 | 1
#define rc(x) (x << 1) + 2

//仿函数
template <class T>
class Less
{
public:
    bool operator()(T &a, T &b)
    {
        return a < b;
    }
};

template <class T, class ORD = Less<T>>
class priority_queue
{
    T *arr;
    size_t size, capacity;

    //扩容
    void Extend()
    {
        size_t new_capacity = capacity + (((capacity >> 1) > 1) ? (capacity >> 1) : 1);
        T *p = (T*)realloc(arr, new_capacity * sizeof(T));
        if (!p)
        {
            std::cout << "malloc error." << std::endl;
            return;
        }
        capacity = new_capacity;
        arr = p;
        p = nullptr;
    }

    //从子结点依次向上排序
    void PushUp(size_t cur)
    {
        if (!cur)
            return;
        size_t pa;
        if (cur & 1)
            pa = (cur - 1) >> 1;
        else
            pa = (cur - 2) >> 1;
        if (ORD()(arr[cur], arr[pa]))
        {
            T t = arr[cur];
            arr[cur] = arr[pa];
            arr[pa] = t;
            PushUp(pa);
        }
    }

    //从父节点依次向下排序
    void PushDown(size_t cur)
    {
        if (lc(cur) >= size)
            return;
        int ne = lc(cur);
        if (rc(cur) < size && ORD()(arr[rc(cur)], arr[lc(cur)]))
            ne = rc(cur);
        if (!ORD()(arr[ne], arr[cur]))
            return;
        T t = arr[cur];
        arr[cur] = arr[ne];
        arr[ne] = t;
        PushDown(ne);
    }

public:
    //获取堆元素个数
    int Size() const
    {
        return size;
    }

    //判空
    bool Empty() const
    {
        return size == 0;
    }

    //获取堆顶元素
    T top() const
    {
        return arr[0];
    }

    //将元素压入堆
    void push(const T x)
    {
        if (size >= capacity)
            Extend();
        arr[size++] = x;
        size_t cur = size - 1;
        PushUp(cur);
    }

    //从堆中弹出元素
    void pop()
    {
        arr[0] = arr[--size];
        PushDown(0);
    }

    //遍历并打印所有元素(层序)
    void _travel()
    {
        for (int i = 0; i < size; i++)
            std::cout << arr[i] << ' ';
        std::cout << std::endl;
    }

    //构造函数
    priority_queue() : arr(nullptr), size(0), capacity(1)
    {
        arr = (T *)malloc(sizeof(T));
    }

    //析构函数
    ~priority_queue()
    {
        if (arr)
            free(arr);
    }
};

#endif

trnode.h

#ifndef _trnode_H
#define _trnode_H
#include <optional>

template <class T>
class TrNode
{
public:
    std::optional<T> e;
    TrNode<T>* ch[2];
    int weight;

    //重载小于号
    bool operator<(const TrNode &b);

    TrNode();
    TrNode(std::optional<T> elem, int w);
    ~TrNode() = default;
};

//重载小于号
template <class T>
bool TrNode<T>::operator<(const TrNode &b)
{
    return this->weight < b.weight;
}

template <class T>
TrNode<T>::TrNode() : e(std::nullopt), weight(0)
{
    ch[0] = nullptr;
    ch[1] = nullptr;
}

template <class T>
TrNode<T>::TrNode(std::optional<T> elem, int w) : e(elem), weight(w)
{
    ch[0] = nullptr;
    ch[1] = nullptr;
}

#endif

huffman_tree.h

#ifndef _huffman_tree_H
#define _huffman_tree_H
#include "trnode.h"
#include "priority_queue.h"
#include <vector>
#include <map>
#include <iostream>

template <class T>
class HuffmanTree
{
    static priority_queue<TrNode<T> *> *q;

    TrNode<T> *arr;
    int idx, size;
    std::map<std::optional<T>, std::string> encode_mp;
    std::map<std::string, std::optional<T>> decode_mp;

    void Update(TrNode<T> *cur, std::string &str);
    void CreateHuffmanTree();

public:
    std::string CreateCode(std::optional<T> e);
    std::string CreateCompleteCode(std::vector<std::optional<T>> v) const;
    std::string CreateCompleteCode(std::string str);
    void PrintAllCodes();
    void PrintAllCodes(std::vector<T> &elems);
    std::string Decode(std::string str);

    HuffmanTree(int n, std::vector<T> &elems, std::vector<int> &weights);
    ~HuffmanTree();
};

template <class T>
priority_queue<TrNode<T> *> *HuffmanTree<T>::q = new priority_queue<TrNode<T> *>();

// dfs更新map对应值
template <class T>
void HuffmanTree<T>::Update(TrNode<T> *cur, std::string &str)
{
    if (!cur->ch[0] && !cur->ch[1])
    {
        encode_mp[cur->e] = str;
        decode_mp[str] = cur->e;
        return;
    }
    if (cur->ch[0])
    {
        str.push_back('0');
        Update(cur->ch[0], str);
        str.pop_back();
    }
    if (cur->ch[1])
    {
        str.push_back('1');
        Update(cur->ch[1], str);
        str.pop_back();
    }
}

//生成哈夫曼树
template <class T>
void HuffmanTree<T>::CreateHuffmanTree()
{
    for (int i = 1; i <= size; i++)
    {
        q->push(&arr[i]);
    }
    while (q->Size() > 1)
    {
        auto a = q->top();
        q->pop();
        auto b = q->top();
        q->pop();
        arr[++idx] = TrNode{(std::optional<T>)std::nullopt, a->weight + b->weight};
        arr[idx].ch[0] = a;
        arr[idx].ch[1] = b;
        q->push(&arr[idx]);
    }
    std::string s = "";
    Update(arr + idx, s);
}

//为一个元素编码
template <class T>
std::string HuffmanTree<T>::CreateCode(std::optional<T> e)
{
    return encode_mp[e];
}

//为一系列元素生成哈夫曼编码
template <class T>
std::string HuffmanTree<T>::CreateCompleteCode(std::vector<std::optional<T>> v) const
{
    std::string ret = "";
    for (auto p : v)
    {
        ret += CreateCode(p);
    }
    return ret;
}

//传参为字符串的重载
template <class T>
std::string HuffmanTree<T>::CreateCompleteCode(std::string str)
{
    return "";
}

//传参为字符串的重载
template <>
std::string HuffmanTree<char>::CreateCompleteCode(std::string str)
{
    std::string ret = "";
    for (char c : str)
    {
        ret += CreateCode(c);
    }
    return ret;
}

//打印所有元素及对应编码
template <class T>
void HuffmanTree<T>::PrintAllCodes()
{
    for (int i = 1; i <= size; i++)
    {
        std::cout << arr[i].e << " : " << encode_mp[arr[i].e] << std::endl;
    }
}

//打印所有元素及对应编码
template <class T>
void HuffmanTree<T>::PrintAllCodes(std::vector<T> &elems)
{
    for (auto elem : elems)
    {
        std::cout << elem << " : " << encode_mp[elem] << std::endl;
    }
}

//为一串哈夫曼编码解码
template <class T>
std::string HuffmanTree<T>::Decode(std::string str)
{
    std::string ret, buf;
    for (auto c : str)
    {
        if (!decode_mp.count(buf))
            buf.push_back(c);
        else
        {
            ret += *decode_mp[buf];
            buf = "";
            buf.push_back(c);
        }
    }
    ret += *decode_mp[buf];
    return ret;
}

template <class T>
HuffmanTree<T>::HuffmanTree(int n, std::vector<T> &elems, std::vector<int> &weights) : size(n), idx(n)
{
    arr = new TrNode<T>[1 << n];
    for (int i = 1; i <= n; i++)
    {
        arr[i].e = elems[i - 1];
        arr[i].weight = weights[i - 1];
    }
    CreateHuffmanTree();
}

template <class T>
HuffmanTree<T>::~HuffmanTree()
{
    free(arr);
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值