哈夫曼树(Huffman树)实现

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

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给频率表

image-20220105081831621

重复第一步:

image-20220105081848649

image-20220105081907259

image-20220105081926271

image-20220105081940474

image-20220105081958021

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

image-20220105082019527

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 <iostream>
#include <queue>
#include <string>
#include <vector>

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<Node*, vector<Node*>, 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;
}

正确输出:
image-20220105003326817

4 参考资料

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

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中实现哈夫曼树Huffman Tree),也称为最优二叉或霍夫曼编码,通常用于数据压缩算法。这里是一个简单的步骤和代码示例: 1. **构建频率表**:首先,收集并统计每个字符在输入序列中的出现频率。 2. **创建节点**:创建一个节点类,包含字符、频率和左右子节点。 ```java class Node { char data; int frequency; Node left, right; // 构造方法和 getters/setters... } ``` 3. **构造初始集合**:将所有字符及其频率放入一个优先队列(最小堆)。 4. **构建哈夫曼树**: - 取出队列中的两个频率最低的节点,合并成一个新的节点,新节点的频率为两个节点之和,左子节点为第一个节点,右子节点为第二个节点。 - 将新节点放回优先队列。 - 重复此过程,直到队列只剩下一个节点,即为哈夫曼树的根节点。 5. **编码过程**:从根节点开始,遍历的过程形成编码规则。如果向左走代表0,向右走代表1。 6. **代码实现**: ```java import java.util.PriorityQueue; public class HuffmanTree { private static PriorityQueue<Node> heap = new PriorityQueue<>((a, b) -> a.frequency - b.frequency); //... 其他方法 public static String compress(char[] input) { // 建立频率表并放入堆 for (char ch : input) { incrementFrequency(ch); } buildHuffmanTree(); // 编码 StringBuilder encoded = new StringBuilder(); buildCode(encoded, root, ""); return encoded.toString(); } private static void buildHuffmanTree() { // 代码省略... } // ...其他方法 } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值