优先队列实现哈夫曼树——中堂系的困难任务

本文介绍了一道与哈夫曼树相关的DP题目,通过分析给出的公式与哈夫曼树DP公式之间的联系,利用优先队列实现了哈夫曼树的构建过程,最终求得最小带权路径和。

 

题目链接CDOJ中堂系的困难任务

题目描述

 

 

 

 

 

 

 

 

 

Sample input and output

Sample InputSample Output
3
3
1 1 1
5
28 26 25 24 1
10
996 901 413 331 259 241 226 209 139 49
5
233
11037

 

按照题目给的公式稍微推一下发现f ( i,  j) = min(f (i -1 ,j+1 ) , f ( i, j/2 ) +b[ i ] ) 这个公式与哈夫曼树的dp式有些相似,

哈夫曼树的dp推导

先对元素从大到小排序。 
设f( i , j )表示现在已经做了前i个元素,还空出来j个叶子节点的最小代价。 
第一种转移显然,就是把第i+1个元素放到一个叶子节点上,f ( i+1,j−1 )=f (i , j )。 
第二种转移就是把当前剩下的叶子节点都再生成出两个节点来,代价是把当前的剩下元素全部加深了一层,所以

f( i,2∗j )=f( i,j )+b[ i+1 ]。    Ans=min(fn,k)

所以题目的公式就是哈夫曼树的推导式;f (n,1) 就是最小带权路径和;

然后看样例发现恰好是这样,逃....ε=ε=ε=┏(゜ロ゜;)┛

所以直接用优先队列模拟堆实现,类比合并果子那个题

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int maxn=1e5+7;
int t;
int a[maxn];
int b[maxn];
int n;
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n;
        priority_queue<long long,vector<long long > ,greater<long long > > q;
        long long cnt=0;
        for(int i=1;i<=n;i++)
            {
                cin>>a[i];
                q.push(a[i]);
                cnt+=a[i];
            }

        long long ans=0;
        while(q.size()>1)
        {
            long long f1=q.top();
            q.pop();
            long long f2=q.top();
            q.pop();
            ans+=f1+f2;
            q.push(f1+f2);
        }
        cout<<ans<<endl;

        //    cout<<ans-cnt<<endl;



    }
    return 0;
}

 

 

哈夫曼树知识补充:

哈夫曼树

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

对于一颗哈夫曼树,我们把所有节点排序,权值大的必定层数较低,f[i][j]代表已经放了i-1个叶子节点,正准备放置Ai,该层还有j个空节点我们可以选择在空节点上放一个叶子节点,从而转移到状态f[i+1][j-1],或者选择移动到下一层,即转移到f[i][j*2],该树权值增大量剩下节点的总权值。

以下是使用 C 语言结合优先队列(最小堆)实现哈夫曼的代码示: ```c #include <stdio.h> #include <stdlib.h> // 定义哈夫曼节点结构 typedef struct HuffmanNode { int data; int freq; struct HuffmanNode *left, *right; } HuffmanNode; // 定义优先队列节点结构 typedef struct PriorityQueueNode { HuffmanNode *huffmanNode; struct PriorityQueueNode *next; } PriorityQueueNode; // 定义优先队列结构 typedef struct { PriorityQueueNode *front; } PriorityQueue; // 创建新的哈夫曼节点 HuffmanNode* newHuffmanNode(int data, int freq) { HuffmanNode* node = (HuffmanNode*)malloc(sizeof(HuffmanNode)); node->data = data; node->freq = freq; node->left = node->right = NULL; return node; } // 创建新的优先队列节点 PriorityQueueNode* newPriorityQueueNode(HuffmanNode *huffmanNode) { PriorityQueueNode* node = (PriorityQueueNode*)malloc(sizeof(PriorityQueueNode)); node->huffmanNode = huffmanNode; node->next = NULL; return node; } // 创建优先队列 PriorityQueue* createPriorityQueue() { PriorityQueue* queue = (PriorityQueue*)malloc(sizeof(PriorityQueue)); queue->front = NULL; return queue; } // 判断优先队列是否为空 int isEmpty(PriorityQueue* queue) { return queue->front == NULL; } // 插入元素到优先队列 void insert(PriorityQueue* queue, HuffmanNode *huffmanNode) { PriorityQueueNode* newNode = newPriorityQueueNode(huffmanNode); if (isEmpty(queue) || huffmanNode->freq < queue->front->huffmanNode->freq) { newNode->next = queue->front; queue->front = newNode; } else { PriorityQueueNode* current = queue->front; while (current->next != NULL && current->next->huffmanNode->freq < huffmanNode->freq) { current = current->next; } newNode->next = current->next; current->next = newNode; } } // 从优先队列中取出最小频率的元素 HuffmanNode* extractMin(PriorityQueue* queue) { if (isEmpty(queue)) return NULL; PriorityQueueNode* temp = queue->front; HuffmanNode* minNode = temp->huffmanNode; queue->front = queue->front->next; free(temp); return minNode; } // 构建哈夫曼 HuffmanNode* buildHuffmanTree(int data[], int freq[], int size) { PriorityQueue* queue = createPriorityQueue(); for (int i = 0; i < size; i++) { insert(queue, newHuffmanNode(data[i], freq[i])); } while (!isEmpty(queue) && queue->front->next != NULL) { HuffmanNode* left = extractMin(queue); HuffmanNode* right = extractMin(queue); HuffmanNode* merged = newHuffmanNode(-1, left->freq + right->freq); merged->left = left; merged->right = right; insert(queue, merged); } return extractMin(queue); } // 前序遍历哈夫曼 void preOrder(HuffmanNode* root) { if (root != NULL) { printf("%d(%d) ", root->data, root->freq); preOrder(root->left); preOrder(root->right); } } // 释放哈夫曼的内存 void freeHuffmanTree(HuffmanNode* root) { if (root != NULL) { freeHuffmanTree(root->left); freeHuffmanTree(root->right); free(root); } } // 释放优先队列的内存 void freePriorityQueue(PriorityQueue* queue) { while (!isEmpty(queue)) { extractMin(queue); } free(queue); } int main() { int data[] = {1, 2, 3, 4}; int freq[] = {5, 9, 12, 13}; int size = sizeof(data) / sizeof(data[0]); HuffmanNode* root = buildHuffmanTree(data, freq, size); printf("Pre-order traversal of Huffman Tree:\n"); preOrder(root); printf("\n"); freeHuffmanTree(root); return 0; } ``` ### 代码说明 1. **哈夫曼节点结构**:定义了 `HuffmanNode` 结构体,包含数据、频率以及左右子节点指针。 2. **优先队列节点结构**:定义了 `PriorityQueueNode` 结构体,包含哈夫曼节点指针和指向下一个优先队列节点的指针。 3. **优先队列结构**:定义了 `PriorityQueue` 结构体,包含一个指向优先队列首节点的指针。 4. **构建哈夫曼**:通过优先队列不断取出最小频率的两个节点,合并成一个新节点,再插入优先队列,直到队列中只剩下一个节点,即为哈夫曼的根节点。 5. **前序遍历**:实现了 `preOrder` 函数用于前序遍历哈夫曼。 6. **内存管理**:实现了 `freeHuffmanTree` 和 `freePriorityQueue` 函数用于释放哈夫曼优先队列的内存。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值