哈夫曼树(Huffman树)原理分析及实现

哈夫曼树(Huffman树)原理分析及实现

创新互联是一家专业提供蒲城企业网站建设,专注与成都网站建设、成都网站设计、HTML5、小程序制作等业务。10年已为蒲城众多企业、政府机构等服务。创新互联专业的建站公司优惠进行中。

1 构造原理

假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:
  (1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
  (2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
  (3)从森林中删除选取的两棵树,并将新树加入森林;
  (4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。

显然对n个权值,构造哈夫曼树树需要合并n-1次,形成的树结点总数为2n-1;

例如:A:60, B:45, C:13 D:69 E:14 F:5 G:3

第一步:找出字符中最小的两个,小的在左边,大的在右边,组成二叉树。

F和G最小,因此如图,从字符串频率计数中删除F与G,并返回G与F的和 8给频率表

重复第一步:

编码规则:添加 0 和 1,规则左0 右1

2 代码实现

根据哈夫曼树的构造原理,为方便实现,我们使用数组来存储每个结点,其命名为Tree;

2.1 节点结构

节点具有以下结构:

//结点结构
struct Node {
    int   val{0};           //节点的值
    Node* left{nullptr};    //节点的左孩子
    Node* right{nullptr};   //节点的右孩子
    Node* parent{nullptr};  //节点的父节点

    explicit Node(int value) : val(value) {}

    Node(int value, Node* pleft, Node* pRight, Node* pParent)
        : val(value), left(pleft), right(pRight), parent(pParent) {}
};

2.2 类的实现

Huffman.h

#include 
#include 
#include 
#include 

using namespace std;

struct Node {
    int   val{0};           //节点的值
    Node* left{nullptr};    //节点的左孩子
    Node* right{nullptr};   //节点的右孩子
    Node* parent{nullptr};  //节点的父节点

    explicit Node(int value) : val(value) {}

    Node(int value, Node* pleft, Node* pRight, Node* pParent)
        : val(value), left(pleft), right(pRight), parent(pParent) {}
};

class Compare  //比较类,用于构造Node*类型的priority_queue
{
public:
    bool operator()(Node* a, Node* b) {
        return a->val > b->val;  //结点的值越小越靠前
    }
};

class HuffmanTree {
public:
    HuffmanTree();

    ~HuffmanTree();

    Node* Create();

    void PreOrder(Node* pNode);

    void InOrder(Node* pNode);

    void PostOrder(Node* pNode);

    void Encode(Node* pNode, string code);

private:
    Node* root;  // 哈夫曼树头

    priority_queue, Compare> nodes;

    void destroyTree(Node* pNode);
};

Hufman.cpp

#include "Huffman.h"

HuffmanTree::HuffmanTree() {
    // priority_queue没有clear
    while (!nodes.empty()) nodes.pop();
    int a[] = {4, 3, 5, 8, 7, 9};
    int len = sizeof(a) / sizeof(a[0]);
    for (int i = 0; i < len; i++) { nodes.push(new Node(a[i])); }
    root = nullptr;
}

HuffmanTree::~HuffmanTree() {
    destroyTree(root);
}

void HuffmanTree::destroyTree(Node* pNode) {
    if (pNode == nullptr)
        return;
    destroyTree(pNode->left);
    destroyTree(pNode->right);
    delete pNode;
}

Node* HuffmanTree::Create() {
    while (nodes.size() > 1) {
        Node* p1 = nodes.top();
        nodes.pop();
        Node* p2 = nodes.top();
        nodes.pop();

        Node* cur  = new Node(p1->val + p2->val);
        cur->left  = p1;
        cur->right = p2;
        p1->parent = cur;
        p2->parent = cur;

        nodes.push(cur);
    }

    root = nodes.top();
    nodes.pop();

    return root;
}

void HuffmanTree::PreOrder(Node* pNode) {
    if (pNode == nullptr)
        return;
    cout << pNode->val << " ";
    PreOrder(pNode->left);
    PreOrder(pNode->right);
}

void HuffmanTree::InOrder(Node* pNode) {
    if (pNode == nullptr)
        return;
    InOrder(pNode->left);
    cout << pNode->val << " ";
    InOrder(pNode->right);
}

void HuffmanTree::PostOrder(Node* pNode) {
    if (pNode == nullptr)
        return;
    PostOrder(pNode->left);
    PostOrder(pNode->right);
    cout << pNode->val << " ";
}

void HuffmanTree::Encode(Node* pNode, string code) {
    //叶子节点的处理
    if (pNode->left == nullptr && pNode->right == nullptr)
        cout << pNode->val << " 被编码为 " << code << endl;

    if (pNode->left) {
        //左子树,编码code添加'0'
        code += "0";
        Encode(pNode->left, code);
        //编码code删除'0'
        code.erase(code.end() - 1);
    }

    if (pNode->right) {
        //左子树,编码code添加'1'
        code += "1";
        Encode(pNode->right, code);
        //编码code删除'1'
        code.erase(code.end() - 1);
    }
}

3 测试代码及输出

int main() {
    HuffmanTree obj;
    Node*       root = obj.Create();
    cout << "先序遍历: ";
    obj.PreOrder(root);
    cout << endl;

    cout << "中序遍历: ";
    obj.InOrder(root);
    cout << endl;

    cout << "后序遍历: ";
    obj.PostOrder(root);
    cout << endl;

    cout << "哈夫曼编码: ";
    obj.Encode(root, "");

    return 0;
}

正确输出:

4 参考资料

1.哈夫曼树算法及C++实现
2.百度百科·哈夫曼树
3.数据结构:Huffman树(哈夫曼树)原理及C++实现


本文题目:哈夫曼树(Huffman树)原理分析及实现
分享网址:http://ybzwz.com/article/dsogojd.html