牛客题解 | 【模板】哈夫曼编码

题目

题目链接

  1. 题目要求

    • \(n\) 种字符的字符串
    • 每种字符出现次数为 \(a_i\)
    • 求哈夫曼编码后的最短长度
  2. 哈夫曼编码原理

    • 出现频率高的字符用短编码
    • 出现频率低的字符用长编码
    • 构建二叉树来得到编码

解题思路

  1. 核心算法

    1. 每次选择两个最小的频率合并
    2. 合并后的频率为两者之和
    3. 重复此过程直到只剩一个节点
    4. 每次合并会使编码长度增加两个数的和
    
  2. 使用优先队列

    • 维护一个最小堆
    • 每次取出两个最小值
    • 将和放回堆中

代码

#include <iostream>
#include <queue>
using namespace std;

int main() {
    int n;
    cin >> n;
    
    // 使用优先队列(最小堆)
    priority_queue<long long, vector<long long>, greater<long long>> pq;
    
    // 读入频率
    for(int i = 0; i < n; i++) {
        long long x;
        cin >> x;
        pq.push(x);
    }
    
    long long result = 0;
    // 当队列中至少有两个元素时
    while(pq.size() > 1) {
        // 取出两个最小值
        long long a = pq.top(); pq.pop();
        long long b = pq.top(); pq.pop();
        // 计算编码长度增量并加入结果
        result += a + b;
        // 将和放回队列
        pq.push(a + b);
    }
    
    cout << result << endl;
    return 0;
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        // 读入字符种数
        int n = sc.nextInt();
        
        // 使用优先队列(最小堆)
        PriorityQueue<Long> pq = new PriorityQueue<>();
        
        // 读入每个字符的频率
        for(int i = 0; i < n; i++) {
            long freq = sc.nextLong();
            pq.offer(freq);
        }
        
        long result = 0;
        
        // 当队列中至少有两个元素时继续合并
        while(pq.size() > 1) {
            // 取出两个最小值
            long a = pq.poll();
            long b = pq.poll();
            
            // 计算编码长度增量并加入结果
            result += a + b;
            
            // 将和放回队列
            pq.offer(a + b);
        }
        
        System.out.println(result);
    }
}
import heapq

n = int(input())
nums = list(map(int, input().split()))

# 构建最小堆
heapq.heapify(nums)

result = 0
# 当堆中至少有两个元素时
while len(nums) > 1:
    # 取出两个最小值
    a = heapq.heappop(nums)
    b = heapq.heappop(nums)
    # 计算编码长度增量并加入结果
    result += a + b
    # 将和放回堆中
    heapq.heappush(nums, a + b)

print(result)

算法及复杂度分析

算法:贪心+优先队列

时间复杂度

  • 建堆:\(\mathcal{O}(n)\)
  • 每次操作:\(\mathcal{O}(\log n)\)
  • 总操作次数:\(n-1\)
  • 总时间复杂度:\(\mathcal{O}(n\log n)\)

空间复杂度

  • 优先队列存储:\(\mathcal{O}(n)\)

注意事项

  1. 数据范围

    • \(n \leq 2 \times 10^5\)
    • \(a_i \leq 10^9\)
    • 需要使用 \(\text{long long}\) 类型
  2. 优先队列使用

    • C++:\(\text{priority_queue}\)
    • Python:\(\text{heapq}\)
    • Java:\(\text{PriorityQueue}\)
  3. 边界情况

    • \(n=1\) 时直接输出 \(0\)
    • 注意数据溢出
posted @ 2025-02-21 17:36  wangxiaoxiao  阅读(33)  评论(0)    收藏  举报