创作灵感: 哈夫曼编码是一种用于数据压缩的常用技术,它可以将字符集中的字符映射到不同长度的二进制编码,以实现高效的数据压缩。本文将介绍一个用C语言编写的程序,演示如何构建哈夫曼树并生成字符的哈夫曼编码。通过这个程序,我们可以学习哈夫曼编码的基本原理以及如何在C语言中实现。
技术笔记要点: 这个C语言程序包括了以下关键部分:
- 定义了哈夫曼树节点(Node)结构体,用于表示字符和频率,以及左右子树的指针。
- 定义了优先队列节点(QueueNode)结构体,用于构建优先队列,其中包含了指向哈夫曼树节点的指针和下一个节点的指针。
- 定义了哈夫曼树(HuffmanTree)结构体,包含指向根节点的指针。
- 实现了创建节点和队列节点的函数,以及创建和操作优先队列的函数。
- 使用优先队列构建哈夫曼树的函数createHuffmanTree,该函数接受字符集、频率和大小作为输入,并返回一个哈夫曼树。
- 实现了打印哈夫曼编码的函数printHuffmanCodes,该函数采用递归方式遍历哈夫曼树,生成字符的哈夫曼编码,并输出结果。
在主函数main中,程序首先获取用户输入的字符集和频率信息,并使用createHuffmanTree函数构建哈夫曼树。然后,使用printHuffmanCodes函数生成并打印字符的哈夫曼编码。
#include <stdio.h> // 引入标准输入输出库
#include <stdlib.h> // 引入标准库,例如内存分配函数
#include <string.h> // 引入字符串操作函数库
// 哈夫曼树节点
typedef struct Node {
char symbol; // 存储字符
int freq; // 存储字符出现的频率
struct Node *left, *right; // 指向左右子树的指针
} Node;
// 优先队列节点
typedef struct QueueNode {
Node *treeNode; // 哈夫曼树结点
struct QueueNode *next; // 指向下一个节点的指针
} QueueNode;
// 哈夫曼树
typedef struct {
Node *root; // 指向哈夫曼树的根的指针
} HuffmanTree;
// 优先队列
typedef struct {
QueueNode *first; // 指向队列中的第一个节点的指针
int size; // 队列的大小
} PriorityQueue;
Node* createNode(char symbol, int freq, Node *left, Node *right) {
Node* node = (Node*)malloc(sizeof(Node)); // 为新的哈夫曼树节点分配内存
node->symbol = symbol; // 设置节点的字符
node->freq = freq; // 设置节点字符的频率
node->left = left; // 设置左子树的指针
node->right = right; // 设置右子树的指针
return node; // 返回新创建的节点的指针
}
QueueNode* createQueueNode(Node *treeNode) {
QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode)); // 为新队列节点分配内存
node->treeNode = treeNode; // 设置哈夫曼树节点
node->next = NULL; // 初始化下一个队列节点的指针
return node; // 返回新创建的队列节点
}
PriorityQueue createPriorityQueue() {
PriorityQueue pq; // 创建一个优先队列结构体变量
pq.first = NULL; // 初始化第一个节点的指针
pq.size = 0; // 初始化队列的大小
return pq; // 返回新创建的优先队列
}
void insertPriorityQueue(PriorityQueue *pq, Node *treeNode) {
QueueNode* newNode = createQueueNode(treeNode); // 创建新的优先队列节点
if (pq->size == 0) { // 如果队列大小为0
pq->first = newNode; // 设置新节点为队列的首个节点
} else {
QueueNode *temp = pq->first;
while (temp->next != NULL && temp->next->treeNode->freq < treeNode->freq) {
temp = temp->next; // 找到合适的插入位置
}
newNode->next = temp->next;
temp->next = newNode; // 插入新的节点
}
pq->size++; // 增加队列的大小
}
Node* dequeuePriorityQueue(PriorityQueue *pq) {
QueueNode *oldFirst = pq->first; // 记住优先队列的头部节点
Node *treeNode = NULL; // 初始化 treeNode
if (oldFirst != NULL) { // 如果队列不为空
treeNode = oldFirst->treeNode; // 提取哈夫曼树节点
pq->first = oldFirst->next; // 更改队列头部为下一个节点
free(oldFirst); // 释放旧的头部节点的内存
pq->size--; // 减少队列的大小
}
return treeNode; // 返回被提取的哈夫曼树节点
}
HuffmanTree createHuffmanTree(char symbols[], int freq[], int size) {
PriorityQueue pq = createPriorityQueue(); // 创建优先权队列
Node *treeNode;
for (int i = 0; i < size; i++) { // 对每个字符
if (freq[i] > 0) { // 如果它在输入中存在(频率大于0)
treeNode = createNode(symbols[i], freq[i], NULL, NULL); // 创建一个新的节点
insertPriorityQueue(&pq, treeNode); // 插入优先队列
}
}
while (pq.size > 1) { // 当优先权队列中有一个以上的节点
Node *left = dequeuePriorityQueue(&pq); // 取出优先权最高的节点作为左子树
Node *right = dequeuePriorityQueue(&pq); // 再取出优先权第二高的节点作为右子树
treeNode = createNode('\0', left->freq + right->freq, left, right); // 创建一个新节点
insertPriorityQueue(&pq, treeNode); // 把新的节点插入优先权队列
}
HuffmanTree ht;
ht.root = dequeuePriorityQueue(&pq); // 最后剩下的节点就是哈夫曼树的根节点
return ht; // 返回哈夫曼树
}
void printHuffmanCodes(Node *root, char *str, int top) {
if (root->left) { // 如果有左子树
str[top] = '0'; // 在编码中加入0
printHuffmanCodes(root->left, str, top + 1); // 递归处理左子树
}
if (root->right) { // 如果有右子树
str[top] = '1'; // 在编码中加入1
printHuffmanCodes(root->right, str, top + 1); // 递归处理右子树
}
if (!(root->left) && !(root->right)) { // 如果是叶子节点(没有子树)
str[top] = '\0'; // 结束编码字符串
printf("%c:%s\n", root->symbol, str); // 输出字符及其哈夫曼编码
}
}
int main() {
int n;
printf("请输入字符集的字符内容,每个元素以空格隔开:"); // 询问用户输入
char line[1000];
fgets(line, sizeof(line), stdin); // 获取用户输入
char *ptr = strtok(line, " ");
char symbols[100];
int i = 0;
while (ptr != NULL) { // 使用空格分割符号
symbols[i++] = ptr[0];
ptr = strtok(NULL, " "); // 继续处理下一个符号
}
n = i; // 计算符号总数
printf("各个字符对应的出现次数为:"); // 询问出现频次
int freq[100];
for (i = 0; i < n; i++) { //对于每个符号
scanf("%d", &freq[i]); // 读取频次
}
getchar(); // 清除后面的 '\n'
HuffmanTree ht = createHuffmanTree(symbols, freq, n); // 创建哈夫曼树
printf("各字符的哈夫曼编码是:\n"); // 输出哈夫曼编码
char str[1000];
printHuffmanCodes(ht.root, str, 0);
return 0; // 返回,表示程序正常退出
}