C语言实现哈夫曼编码和解码

创作灵感: 哈夫曼编码是一种常用的数据压缩技术,它通过将不同字符映射到不同长度的二进制编码来实现数据压缩。本文将介绍一个用C语言编写的程序,演示如何构建哈夫曼树、生成字符的哈夫曼编码,以及如何解码已编码的数据。通过这个程序,我们可以学习哈夫曼编码的实现原理以及如何在C语言中应用它。

技术笔记要点: 这个C语言程序包括以下关键部分:

  1. 定义了哈夫曼树节点(Node)结构体,用于表示字符和频率,以及左右子树的指针。
  2. 定义了优先队列节点(QueueNode)结构体,用于构建优先队列,其中包含了指向哈夫曼树节点的指针和下一个节点的指针。
  3. 定义了哈夫曼树(HuffmanTree)结构体,包含指向根节点的指针。
  4. 实现了创建节点和队列节点的函数,以及创建和操作优先队列的函数。
  5. 使用优先队列构建哈夫曼树的函数createHuffmanTree,该函数接受字符集、频率和大小作为输入,并返回一个哈夫曼树。
  6. 实现了打印哈夫曼编码的函数printHuffmanCodes,该函数采用递归方式遍历哈夫曼树,生成字符的哈夫曼编码,并输出结果。
  7. 实现了解码哈夫曼编码的函数decodeHuffmanTree,该函数接受哈夫曼树的根节点和编码字符串作为输入,将编码字符串解码为原始字符并输出。

在主函数main中,程序首先获取用户输入的字符集和频率信息,然后使用createHuffmanTree函数构建哈夫曼树。接下来,使用printHuffmanCodes函数生成并打印字符的哈夫曼编码。然后,程序询问用户输入要解码的编码数据,使用decodeHuffmanTree函数解码并输出原始字符。

通过这个程序,我们可以学习哈夫曼编码的实现和应用,以及如何在C语言中构建和操作哈夫曼树。这些知识对于数据压缩和编码领域非常重要,希望本文可以帮助你更深入地理解哈夫曼编码的工作原理。

#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 {
        if (pq->first->treeNode->freq >= treeNode->freq) { // 如果首个节点的频率大于等于新节点的频率
            newNode->next = pq->first; // 新节点插入至首位
            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); // 输出字符及其哈夫曼编码
    }
}

void decodeHuffmanTree(Node* root, const char* str) {
    Node* curr = root;
    while (*str) {
        if (*str == '0') // 如果遇到'0',移动到左子节点
            curr = curr->left;
        else
            curr = curr->right; // 否则移动到右子节点
        if (curr->left == NULL && curr->right == NULL) { // 如果到达叶节点
            printf("%c", curr->symbol); // 输出叶节点符号
            curr = root; // 回到根节点继续解码
        }
        str++;
    }
    printf("\n"); // 新行
}

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);

    printf("输入发送的编码。以‘#’结束:"); // 询问编码
    char encodedData[1000];
    fgets(encodedData, sizeof(encodedData), stdin); // 获取编码
    encodedData[strcspn(encodedData, "\n")] = 0;  // 删除末尾的 '\n'
    printf("破译后的字符为:"); // 输出破译结果
    decodeHuffmanTree(ht.root, encodedData); // 破译编码

    return 0; // 返回,表示程序正常退出
}

 

 

 

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言可以通过构建哈夫曼树实现编码解码。下面是一个简单的实现步骤: 1. 定义哈夫曼树的结构体,包括权值、字符、左右子树等信息。 2. 构建哈夫曼树,首先需要统计每个字符出现的频率,然后将每个字符作为一个叶子节点构建一棵树。接着,将这些树按照权值从小到大排序,每次取出权值最小的两棵树合并成一棵新树,直到只剩下一棵树为止。 3. 根据构建好的哈夫曼树生成编码表,即将每个字符的编码存储在一个数组中。 4. 对需要编码的字符串进行编码,即将每个字符替换成对应的编码。 5. 对编码后的字符串进行解码,即根据编码表将编码还原成原始字符串。 下面是一个简单的C语言实现哈夫曼树编码解码的代码示例: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_TREE_HT 100 // 定义哈夫曼树节点结构体 struct MinHeapNode { char data; unsigned freq; struct MinHeapNode *left, *right; }; // 定义哈夫曼树结构体 struct MinHeap { unsigned size; unsigned capacity; struct MinHeapNode **array; }; // 创建一个新的哈夫曼树节点 struct MinHeapNode* newNode(char data, unsigned freq) { struct MinHeapNode* node = (struct MinHeapNode*) malloc(sizeof(struct MinHeapNode)); node->left = node->right = NULL; node->data = data; node->freq = freq; return node; } // 创建一个新的哈夫曼树 struct MinHeap* createMinHeap(unsigned capacity) { struct MinHeap* minHeap = (struct MinHeap*) malloc(sizeof(struct MinHeap)); minHeap->size = 0; minHeap->capacity = capacity; minHeap->array = (struct MinHeapNode**) malloc(minHeap->capacity * sizeof(struct MinHeapNode*)); return minHeap; } // 交换两个哈夫曼树节点 void swapMinHeapNode(struct MinHeapNode** a, struct MinHeapNode** b) { struct MinHeapNode* t = *a; *a = *b; *b = t; } // 维护最小堆的性质 void minHeapify(struct MinHeap* minHeap, int idx) { int smallest = idx; int left = 2 * idx + 1; int right = 2 * idx + 2; if (left < minHeap->size && minHeap->array[left]->freq < minHeap->array[smallest]->freq) smallest = left; if (right < minHeap->size && minHeap->array[right]->freq < minHeap->array[smallest]->freq) smallest = right; if (smallest != idx) { swapMinHeapNode(&minHeap->array[smallest], &minHeap->array[idx]); minHeapify(minHeap, smallest); } } // 判断最小堆是否只有一个节点 int isSizeOne(struct MinHeap* minHeap) { return (minHeap->size == 1); } // 从最小堆中取出最小的节点 struct MinHeapNode* extractMin(struct MinHeap* minHeap) { struct MinHeapNode* temp = minHeap->array[0]; minHeap->array[0] = minHeap->array[minHeap->size - 1]; --minHeap->size; minHeapify(minHeap, 0); return temp; } // 插入一个新的节点到最小堆中 void insertMinHeap(struct MinHeap* minHeap, struct MinHeapNode* minHeapNode) { ++minHeap->size; int i = minHeap->size - 1; while (i && minHeapNode->freq < minHeap->array[(i - 1) / 2]->freq) { minHeap->array[i] = minHeap->array[(i - 1) / 2]; i = (i - 1) / 2; } minHeap->array[i] = minHeapNode; } // 判断一个节点是否是叶子节点 int isLeaf(struct MinHeapNode* root) { return !(root->left) && !(root->right); } // 创建一个最小堆并插入所有的字符 struct MinHeap* createAndBuildMinHeap(char data[], int freq[], int size) { struct MinHeap* minHeap = createMinHeap(size); for (int i = 0; i < size; ++i) minHeap->array[i] = newNode(data[i], freq[i]); minHeap->size = size; for (int i = (minHeap->size - 1) / 2; i >= 0; --i) minHeapify(minHeap, i); return minHeap; } // 构建哈夫曼树 struct MinHeapNode* buildHuffmanTree(char data[], int freq[], int size) { struct MinHeapNode *left, *right, *top; struct MinHeap* minHeap = createAndBuildMinHeap(data, freq, size); while (!isSizeOne(minHeap)) { left = extractMin(minHeap); right = extractMin(minHeap); top = newNode('$', left->freq + right->freq); top->left = left; top->right = right; insertMinHeap(minHeap, top); } return extractMin(minHeap); } // 生成哈夫曼编码表 void printCodes(struct MinHeapNode* root, int arr[], int top) { if (root->left) { arr[top] = 0; printCodes(root->left, arr, top + 1); } if (root->right) { arr[top] = 1; printCodes(root->right, arr, top + 1); } if (isLeaf(root)) { printf("%c: ", root->data); for (int i = 0; i < top; ++i) printf("%d", arr[i]); printf("\n"); } } // 对字符串进行哈夫曼编码 void encode(struct MinHeapNode* root, char* str) { int n = strlen(str); int arr[MAX_TREE_HT], top = 0; for (int i = 0; i < n; ++i) { struct MinHeapNode* p = root; while (!isLeaf(p)) { if (str[i] == '0') p = p->left; else p = p->right; } printf("%c", p->data); } } // 对哈夫曼编码进行解码 void decode(struct MinHeapNode* root, char* str) { int n = strlen(str); struct MinHeapNode* p = root; for (int i = 0; i < n; ++i) { if (str[i] == '0') p = p->left; else p = p->right; if (isLeaf(p)) { printf("%c", p->data); p = root; } } } // 主函数 int main() { char arr[] = {'A', 'B', 'C', 'D', 'E'}; int freq[] = {5, 4, 3, 2, 1}; int size = sizeof(arr) / sizeof(arr[0]); struct MinHeapNode* root = buildHuffmanTree(arr, freq, size); int arrCode[MAX_TREE_HT], top = 0; printCodes(root, arrCode, top); char str[] = "ABCD"; printf("Encoded string is: "); encode(root, str); printf("\n"); char code[] = "110100011"; printf("Decoded string is: "); decode(root, code); printf("\n"); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值