#include<stdio.h> #include<stdlib.h> #include<string.h> #define MAX_CODE_LEN 100

// 定义链表节点结构体 typedef struct ListNode { int c; // 存储字符(使用 int 类型,能够正确处理特殊字符) int frequency; // 存储字符出现频度 char *code; // 存储字符编码(在后续实现哈夫曼编码时使用) int order; // 存储节点插入先后顺序 struct ListNode *parent; struct ListNode *left; struct ListNode *right; struct ListNode *next; // 存储下一个节点指针 int frequencyCount; // 存储节点个数计数器 } ListNode, HuffmanTree;

// 将一个字符插入到链表中,如果已存在则更新频度 void insert(ListNode *head, int c) { ListNode *p = head; while (p->next != NULL) { if (p->next->c == c) { // 如果已存在该字符,则更新频度 p->next->frequency++; return; } p = p->next; } ListNode *newNode = (ListNode *) malloc(sizeof(ListNode)); // 新建一个节点 newNode->c = c; newNode->frequency = 1; newNode->code = NULL; newNode->next = NULL; newNode->order = ++head->frequencyCount; // 记录节点插入的先后顺序 p->next = newNode; // 将新节点插入到链表尾部 }

// 对链表进行选择排序,按照字符频度和插入先后顺序排序 void sort(ListNode *head) { ListNode *p, *q; int tmpc; // 定义临时变量存储字符 int tmpfrequency, tmporder; p = head->next; while(p != NULL) { q = p->next; while(q != NULL) { if(p->frequency < q->frequency || (p->frequency == q->frequency && p->order > q->order)) { // 根据频度和先后顺序进行比较 // 如果 p 节点的频度小于 q 节点的频度,或者 p 和 q 节点的频度相等但 p 的插入先后顺序大于 q 的,则交换两个节点的数据域和指针域 tmpc = q->c; q->c = p->c; p->c = tmpc; tmpfrequency = q->frequency; q->frequency = p->frequency; p->frequency = tmpfrequency; tmporder = q->order; q->order = p->order; p->order = tmporder; p->code += q->code - (q->code = p->code); } q = q->next; } p = p->next; } }

// 建立哈夫曼树 HuffmanTree *buildHuffmanTree(ListNode *head) { // 根据链表中的节点建立哈夫曼树 // 首先将链表中的节点转换为哈夫曼树的叶子节点 // 然后依次合并叶子节点和非叶子节点,直至合并成根节点 // 合并两个节点的过程中,将两个节点的频度之和作为新节点的频度 // 新节点的左子树为频度较小的节点,右子树为频度较大的节点

// 先将链表中的节点转换为哈夫曼树的叶子节点
int n = 0; // 统计叶子节点个数
ListNode *p = head->next;
while (p != NULL) {
    n++;
    p = p->next;
}
HuffmanTree **leaves = (HuffmanTree **) malloc(n * sizeof(HuffmanTree *)); // 动态分配叶子节点指针数组
p = head->next;
for (int i = 0; i < n; i++) {
    leaves[i] = (HuffmanTree *) malloc(sizeof(HuffmanTree)); // 新建一个叶子节点
    leaves[i]->c = p->c;
    leaves[i]->frequency = p->frequency;
    leaves[i]->code = NULL;
    leaves[i]->parent = NULL;
    leaves[i]->left = NULL;
    leaves[i]->right = NULL;
    p = p->next;
}

// 依次合并叶子节点和非叶子节点
for (int i = 0; i < n - 1; i++) {
    // 找到频度最小的两个节点
    int min1, min2;
    min1 = min2 = -1;
    for (int j = 0; j < n + i; j++) {
        if (leaves[j]->parent == NULL) { // 如果该节点还没有父节点,说明它还没有被合并过
            if (min1 == -1 || leaves[j]->frequency < leaves[min1]->frequency) {
                min2 = min1;
                min1 = j;
            } else if (min2 == -1 || leaves[j]->frequency < leaves[min2]->frequency) {
                min2 = j;
            }
        }
    }

    // 新建一个节点,将两个子节点合并到该节点中
    HuffmanTree *newNode = (HuffmanTree *) malloc(sizeof(HuffmanTree));
    newNode->frequency = leaves[min1]->frequency + leaves[min2]->frequency;
    newNode->code = NULL;
    newNode->parent = NULL;
    newNode->left = leaves[min1];
    newNode->right = leaves[min2];
    leaves[min1]->parent = newNode;
    leaves[min2]->parent = newNode;

    // 将新节点插入到叶子节点数组中
    leaves[n + i] = newNode;
}

// 最后一个节点即为根节点
return leaves[n * 2 - 2];

}

// 遍历哈夫曼树,获取叶子节点的哈夫曼编码 void traverseHuffmanTree(HuffmanTree *tree, char *code, int depth) { // tree:当前遍历的节点 // code:当前遍历的节点所对应的哈夫曼编码(字符串形式) // depth:当前遍历的节点的深度

// 如果遍历到了叶子节点,则将其对应的哈夫曼编码保存到节点中
if (tree->left == NULL && tree->right == NULL) {
    tree->code = (char *) malloc((depth + 1) * sizeof(char));
    strcpy(tree->code, code);
} else {
    // 遍历左子树
    code[depth] = '0';
    traverseHuffmanTree(tree->left, code, depth + 1);

    // 遍历右子树
    code[depth] = '1';
    traverseHuffmanTree(tree->right, code, depth + 1);
}

}

int main() { ListNode *head = (ListNode *) malloc(sizeof(ListNode)); head->next = NULL; head->frequency = 0; // 初始化节点计数器 int c; while ((c = getchar()) != EOF) { //使用 getchar() 函数读取输入字符流 insert(head, c); } sort(head); HuffmanTree *tree = buildHuffmanTree(head); // 建立哈夫曼树 char code[MAX_CODE_LEN]; // 用于存储哈夫曼编码的字符数组 traverseHuffmanTree(tree, code, 0); // 遍历哈夫曼树,获取叶子节点的哈夫曼编码

ListNode *p = head->next;
while (p != NULL) {
    if (p->c == '\n') { // 对换行符进行特殊处理
        printf(''\n' %d %s\n', p->frequency, p->parent->code);
    } else { // 其他字符按照 ASCII 码输出
        printf(''%c' %d %s\n', p->c, p->frequency, p->parent->code);
    }
    p = p->next;
}
printf("%d\n", tree->frequency); // 输出哈夫曼树的带权路径长度

return 0;
C语言实现哈夫曼编码:字符统计、排序、构建哈夫曼树并输出编码

原文地址: https://www.cveoy.top/t/topic/ojeQ 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录