哈夫曼树的最小堆实现

哈夫曼树

定义

带权路径长度(WPL):设二叉树有n个叶子结点,每个叶子节点带有权值Wk,从根结点到每个叶子节点的长度为Lk,则每个叶子结点的带权路径长度之和就是:WPL=∑k=1nWkLkWPL=\sum_{k=1}^{n}W_kL_kWPL=k=1nWkLk

特点

  1. 没有度为1的结点
  2. n个叶子结点的哈夫曼树共有2n-1个结点
  3. 哈夫曼树的任意非叶子结点的左右子树交换后仍是哈夫曼树
  4. 同一组权值,存在不同构的哈夫曼树

图解

初始

1
3
5
8

第一次合并

1
3
4
5
8

第二次合并

1
3
4
5
8
9

第三次合并

1
3
4
5
8
9
17

模板

根据给定的n个权值,构造n棵只有根结点的二叉树。通过最小堆每次选取两棵根结点权值最小的树作为左右子树,构造一棵新的二叉树,并将其插入最小堆中。重复上述步骤,直到只有一棵树为止,这棵树就是哈夫曼树。

#include <iostream>
using namespace std;

#define MaxSize 1000
int A[] = {1, 3, 5, 8};
int A_length = 4;

struct TreeNode
{
    int weight;
    TreeNode *left;
    TreeNode *right;
};
struct MinHeap
{
    TreeNode **data; //这是一个数组,每个元素的类型为(TreeNode*),是指向某个哈夫曼树的指针
    int size;
    int capacity;
};

MinHeap *CreateHeap();                   // 初始化堆
TreeNode *CreateHT();                    // 初始化哈夫曼树
TreeNode *Delete(MinHeap *H);            // 删除最小堆元素
void Insert(MinHeap *H, TreeNode *Huff); // 插入最小堆元素
void PreOrderTraversal(TreeNode *Huff);  // 先序遍历
void BuildMinHeap(MinHeap *H);           // 建堆
TreeNode *Huffman(MinHeap *H);           // 哈夫曼树的构建

int main()
{
    MinHeap *H;
    TreeNode *Huff;
    H = CreateHeap();
    Huff = Huffman(H);
    PreOrderTraversal(Huff);
    system("pause");
    return 0;
}

// 初始化堆
MinHeap *CreateHeap()
{
    MinHeap *H;
    H = new MinHeap;
    H->data = new TreeNode *[MaxSize + 1]; // 每个元素的类型为(TreeNode*)
    H->capacity = MaxSize;
    H->size = 0;
    // 给堆设置哨兵,哨兵要小于堆内所有值
    TreeNode *Huff;
    Huff = CreateHT();
    Huff->weight = INT_MIN;
    H->data[0] = Huff;
    return H;
}

// 初始化哈夫曼树
TreeNode *CreateHT()
{
    TreeNode *Huff;
    Huff = new TreeNode;
    Huff->weight = 0;
    Huff->left = NULL;
    Huff->right = NULL;
    return Huff;
}

// 插入最小堆元素(哈夫曼树)
void Insert(MinHeap *H, TreeNode *Huff)
{
    int weight = Huff->weight;
    int i = ++H->size;
    for (; H->data[i / 2]->weight > weight; i /= 2)
    {
        H->data[i] = H->data[i / 2];
    }
    H->data[i] = Huff;
}

// 删除最小堆元素
TreeNode *Delete(MinHeap *H)
{
    int parent, child;
    TreeNode *T = H->data[1];
    TreeNode *tmp = H->data[H->size--];
    for (parent = 1; parent * 2 <= H->size; parent = child)
    {
        child = 2 * parent;
        if ((child != H->size) && (H->data[child + 1]->weight < H->data[child]->weight))
            child++;
        if (H->data[child]->weight >= tmp->weight)
            break;
        else
            H->data[parent] = H->data[child];
    }
    H->data[parent] = tmp;
    return T;
}

// 建堆
void BuildMinHeap(MinHeap *H)
{
    TreeNode *Huff;
    for (int i = 0; i < A_length; i++)
    {
        Huff = CreateHT();
        Huff->weight = A[i];
        Insert(H, Huff);
    }
}

void PreOrderTraversal(TreeNode *Huff)
{
    if (Huff)
    {
        cout << Huff->weight << " ";
        PreOrderTraversal(Huff->left);
        PreOrderTraversal(Huff->right);
    }
}

//构建哈夫曼树
TreeNode *Huffman(MinHeap *H)
{
    TreeNode *T;
    BuildMinHeap(H);
    int times = H->size;
    // 做times-1次合并
    for (int i = 1; i < times; i++)
    {
        T = new TreeNode;
        T->left = Delete(H);
        T->right = Delete(H);
        T->weight = T->left->weight + T->right->weight;
        Insert(H, T);
    }
    T = Delete(H);
    return T;
}
### 如何使用最小构建哈夫曼 构建哈夫曼的核心思想是利用贪心算法,通过最小(优先队列)选择频率最低的两个节点进行合并,直到所有节点合并为一棵。以下是实现该过程的详细说明和代码示例。 #### 构建哈夫曼的步骤 1. 初始化:将每个字符及其频率作为单独的节点插入到最小中。 2. 合并操作:从中取出频率最小的两个节点,创建一个新的内部节点,其频率为这两个节点频率之和,并将新节点重新插入中。 3. 重复上述操作,直到中只剩下一个节点,即为哈夫曼的根节点。 #### 示例代码 以下是一个使用 C++ 实现哈夫曼构建代码: ```cpp #include <iostream> #include <queue> #include <vector> #include <string> #include <unordered_map> using namespace std; // 定义哈夫曼节点结构 struct HuffmanNode { char character; // 字符 int frequency; // 频率 HuffmanNode* left; // 左子节点 HuffmanNode* right; // 右子节点 HuffmanNode(char c, int f) : character(c), frequency(f), left(nullptr), right(nullptr) {} }; // 自定义比较函数,用于最小 struct Compare { bool operator()(HuffmanNode* l, HuffmanNode* r) { return l->frequency > r->frequency; } }; // 打印哈夫曼编码 void printHuffmanCodes(HuffmanNode* root, string str) { if (!root) return; if (root->left == nullptr && root->right == nullptr) { cout << root->character << ": " << str << endl; } printHuffmanCodes(root->left, str + "0"); printHuffmanCodes(root->right, str + "1"); } // 构建哈夫曼 HuffmanNode* buildHuffmanTree(const unordered_map<char, int>& freqMap) { priority_queue<HuffmanNode*, vector<HuffmanNode*>, Compare> minHeap; // 将每个字符及其频率加入最小 for (const auto& pair : freqMap) { minHeap.push(new HuffmanNode(pair.first, pair.second)); } // 不断合并中的节点 while (minHeap.size() != 1) { HuffmanNode* left = minHeap.top(); minHeap.pop(); HuffmanNode* right = minHeap.top(); minHeap.pop(); HuffmanNode* merged = new HuffmanNode('\0', left->frequency + right->frequency); merged->left = left; merged->right = right; minHeap.push(merged); } return minHeap.top(); } int main() { // 输入字符及其频率 unordered_map<char, int> freqMap = { {'A', 45}, {'B', 13}, {'C', 12}, {'D', 16}, {'E', 9}, {'F', 5} }; // 构建哈夫曼 HuffmanNode* root = buildHuffmanTree(freqMap); // 输出哈夫曼编码 cout << "Huffman Codes:" << endl; printHuffmanCodes(root, ""); return 0; } ``` #### 代码解释 - **HuffmanNode 结构**:表示哈夫曼的节点,包含字符、频率以及左右子节点指针。 - **Compare 结构**:用于定义最小的比较规则,确保频率较低的节点优先级更高。 - **buildHuffmanTree 函数**:根据输入的频率表构建哈夫曼。 - **printHuffmanCodes 函数**:递归遍历哈夫曼,输出每个字符对应的编码[^4]。 ### 注意事项 在实际应用中,为了节省存储空间,可以将哈夫曼的编码结果保存为二进制文件或字符串形式。此外,如果需要处理大量数据,可以考虑优化内存管理以避免过多的动态分配[^1]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值