HuffmanTree的创建、编码、解码操作

#include <iostream>
#include <cstdlib>
#include <cstring>
#pragma warning(disable:4996) //忽略strcpy的错误
using namespace std;

typedef struct HTNode {
	char c; //存储的字符
	int weight; //权值
	int parent, lchild, rchild; //双亲序号 左孩子和右孩子序号
}HTNode, * HuffmanTree;
typedef char** HuffmanCode; //把HuffmanCode定义为指向char的指针的指针 相当于一个二维数组 列存放每个节点 行存放每个结点的字符

int min(HuffmanTree HT, int k) { //求出权值最小的一个结点 
	int i = 0;
	int minNum, minNum_weight; //分别存放权值最小的结点序号和对应的权值

	while (HT[i].parent != 0) i++;  //找到第一个权值为0的结点

	minNum = i; minNum_weight = HT[i].weight;

	for (; i <= k; i++) {
		if (HT[i].parent == 0 && HT[i].weight < minNum_weight) {
			minNum = i;
			minNum_weight = HT[i].weight;
		}
	} //for

	HT[minNum].parent = 1; //找出最小权值的结点之后 将其parent域置为1 不需要知道确切的parent值 做个标记以便区分即可
	return minNum;
}

void Select(HuffmanTree HT, int k, int& s1, int& s2) {  //求出权值最小的两个结点 调用min()函数两次
	s1 = min(HT, k);
	s2 = min(HT, k);
}

HuffmanTree CreatHuffmanTree(int n) {  //创建HuffmanTree
	int s1, s2;
	int m = 2 * n - 1; //n代表有多少个根节点要合并 m代表合并完总结点的个数 
	if (n <= 1) return 0;
	HuffmanTree HT = new HTNode[m + 1]; //0号不用 多分配一个存储单元
	for (int i = 1; i <= m; i++) { //初始化操作
		HT[i].lchild = 0;
		HT[i].rchild = 0;
		HT[i].parent = 0;
	}
	for (int i = 1; i <= n; i++) { //赋值操作,输入每个结点的字符以及权值
		cin >> HT[i].c >> HT[i].weight;
	}
	//以上为初始化操作  接下来生成HuffmanTree
	for (int i = n + 1; i <= m; i++) {
		Select(HT, i - 1, s1, s2); //在HT[k](1<=k<=i-1)中找到两个权值最小的且parent值为0的结点,将这两个结点在数组中的序号值用s1 s2返回
		HT[s1].parent = i; HT[s2].parent = i; //从数组中删除s1 s2,利用对其parent赋值的方法
		HT[i].lchild = s1; HT[i].rchild = s2;  //对新节点的左右孩子赋值
		HT[i].weight = HT[s1].weight + HT[s2].weight; //新结点的权值等于左右孩子之和
	}
	return HT;
}

void HTPrint(HuffmanTree HT, int n) { //打印HuffmanTree
	cout << "weight" << "\t\t" << "parent" << "\t\t" << "lchild" << "\t\t" << "rchild" << endl;
	for (int i = 1; i <= 2 * n - 1; i++)
		cout << HT[i].weight << "\t\t" << HT[i].parent << "\t\t" << HT[i].lchild << "\t\t" << HT[i].rchild << endl;
}

void CreatHuffCode(HuffmanTree HT, HuffmanCode& HC, int n) {  //从叶子逆方向回溯到根节点求出原始结点的编码
	int location; //code字符数组的下标 每次循环从最后一位开始
	int f, c;
	char* code; //临时存放编码的字符数组
	HC = new char* [n + 1]; //n个结点分配n+1个头指针
	code = new char[n];
	code[n - 1] = '\0'; //字符数组编码结束符
	for (int i = 1; i <= n; i++) {  //从1->n 逐个字符求编码
		location = n - 1; c = i; f = HT[i].parent;
		while (f != 0) {
			--location;
			if (HT[f].lchild == c) code[location] = '0';
			else				   code[location] = '1';
			c = f; f = HT[f].parent; //继续执行 类似于链表的-》next
		} //while
		HC[i] = new char[n - location]; //为二维数组的行分配空间
		strcpy(HC[i], &code[location]); //注意strcpy的用法 里面是地址 HC[i]本身就是地址 对于code[]需要取地址&, 而且code[location]可以从有编码的位置开始赋值 前面的空都不会复制过去
	} //for

}

void HCPrint(HuffmanTree HT, HuffmanCode HC, int n) { //打印HuffmanTree中每个原始结点的编码 
	cout << "字符对应的编码为:" << endl;
	for (int i = 1; i <= n; i++) {
		cout << "字符" << HT[i].c << "的编码为:" << HC[i] << endl;
	}
}

void Encode(HuffmanTree HT, HuffmanCode HC, char str[], int n, int cnt) { //对输入的字符进行编码操作 注意:输入的字符仅限于上述HuffmanTree中的原始字符
	for (int i = 0; i < cnt; i++) {
		for (int j = 1; j <= n; j++) { //每个都比较一遍
			if (str[i] == HT[j].c) {
				cout << HC[j];
				break;
			}
		}
	}
}

void DeCode(HuffmanTree	HT, char charCode[], int n) { //解码操作 利用对编码后的字符从头到尾 在对应的HuffmanTree中深度搜索的方式得到解码字符
	int intCode[50];
	for (int i = 0; i < strlen(charCode); i++) {
		if (charCode[i] == '0') intCode[i] = 0;
		else				    intCode[i] = 1; //对整型数组进行赋值初始化
	}
	int cnt = 0; //计数已经解码过的个数
	while (cnt < strlen(charCode)) {
		int j = 2 * n - 1;
		while (HT[j].lchild != 0 && HT[j].rchild != 0) {
			if (intCode[cnt] == 0) j = HT[j].lchild;
			if (intCode[cnt] == 1) j = HT[j].rchild;
			cnt++; //一次循环结束 计数加一
		}
		cout << HT[j].c;
	}
}

int main() {
	int n; char Receivechar[20];
	char charCode[50]; //接受要解码的01字符串
	HuffmanCode HC;
	cout << "请输入原根结点数n:";
	cin >> n;

	HuffmanTree HT = CreatHuffmanTree(n); //创建HuffmanTree 
	CreatHuffCode(HT, HC, n); //生成每个原始结点的HuffmanCode 

	HTPrint(HT, n); //打印HuffmanTree 
	HCPrint(HT, HC, n); //打印每个原始结点的HuffmanCode 

	cout << "请输入要编码的字符:" << endl;
	int i = 0;
	while ((Receivechar[i] = getchar()) != '#') i++; //i表示多少个字符
	cout << "编码为:";
	Encode(HT, HC, Receivechar, n, i); //输出编码 
	cout << endl;

	getchar(); //用于接收上一步输入的回车符 防止下面的gets_s接收到这个回车符从而被跳过!!!
	cout << "请输入要解码的01串:";
	gets_s(charCode); //获取字符串
	cout << "解码为:";
	DeCode(HT, charCode, n);
}

 2021-11-17补充:

以上没有文件操作,如果想把字符的编码和解码内容保存到文件中,可以利用几句文件的操作。

例如:

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

int main() {
	ofstream  fout;
	fout.open("C:/Users/xxx/Desktop/xxx.txt",ios::out);
	
	int n1 = 100;
	char name[] = "xxx";
	fout<<n1<<endl;
	fout<<name<<endl;
	
	fout.close();
} 

文件还是要好好学,数据多的时候还能靠控制台输入吗。。。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Huffman编码是一种无损压缩算法,常用于数据传输和存储中。下面是C语言实现Huffman编码解码的基本思路: Huffman编码: 1. 统计字符出现频率,构建字符频率表; 2. 将字符频率表中的字符和频率信息构建成一棵Huffman树; 3. 遍历Huffman树,给每个字符生成对应的Huffman编码; 4. 将编码写入文件。 Huffman解码: 1. 读取压缩文件中的编码信息和字符频率表; 2. 根据字符频率表构建Huffman树; 3. 遍历Huffman树,根据编码信息还原原始字符串。 以下是C语言实现Huffman编码解码的参考代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_TREE_HT 100 // Huffman树节点结构体 struct MinHeapNode { char data; unsigned freq; struct MinHeapNode *left, *right; }; // Huffman编码表结构体 struct HuffmanCode { char data; char *code; }; // 最小优先队列结构体 struct MinHeap { unsigned size; unsigned capacity; struct MinHeapNode **array; }; // 创建一个新的Huffman树节点 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); } } // 检查堆大小是否为1 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; } // 构建最小优先队列 void buildMinHeap(struct MinHeap* minHeap) { int n = minHeap->size - 1; int i; for (i = (n - 1) / 2; i >= 0; --i) minHeapify(minHeap, i); } // 判断是否是叶子节点 int isLeaf(struct MinHeapNode* root) { return !(root->left) && !(root->right); } // 创建一个最小优先队列并构建Huffman树 struct MinHeap* createAndBuildMinHeap(char data[], int freq[], int size) { int i; struct MinHeap* minHeap = createMinHeap(size); for (i = 0; i < size; ++i) minHeap->array[i] = newNode(data[i], freq[i]); minHeap->size = size; buildMinHeap(minHeap); return minHeap; } // 构建Huffman树 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); } // 生成Huffman编码 void generateCodes(struct MinHeapNode* root, char *code, int top, struct HuffmanCode *huffmanCode) { if (root->left) { code[top] = '0'; generateCodes(root->left, code, top + 1, huffmanCode); } if (root->right) { code[top] = '1'; generateCodes(root->right, code, top + 1, huffmanCode); } if (isLeaf(root)) { huffmanCode[root->data].data = root->data; huffmanCode[root->data].code = (char*)malloc((top + 1) * sizeof(char)); memcpy(huffmanCode[root->data].code, code, (top + 1) * sizeof(char)); } } // 打印Huffman编码表 void printHuffmanCodes(struct HuffmanCode *huffmanCode, int size) { int i; printf("Huffman编码表:\n"); for (i = 0; i < size; ++i) printf("%c: %s\n", huffmanCode[i].data, huffmanCode[i].code); } // 压缩文件 void compressFile(char *fileName, struct HuffmanCode *huffmanCode, int size) { FILE *fpIn, *fpOut; char ch, *code; int i, j, len; fpIn = fopen(fileName, "r"); if (fpIn == NULL) { printf("打开文件失败!\n"); return; } fpOut = fopen("compressed.dat", "wb"); fwrite(&size, sizeof(int), 1, fpOut); for (i = 0; i < size; ++i) { fwrite(&huffmanCode[i].data, sizeof(char), 1, fpOut); len = strlen(huffmanCode[i].code); fwrite(&len, sizeof(int), 1, fpOut); fwrite(huffmanCode[i].code, sizeof(char), len, fpOut); } code = (char*)malloc(MAX_TREE_HT * sizeof(char)); while ((ch = fgetc(fpIn)) != EOF) { for (i = 0; i < size; ++i) { if (huffmanCode[i].data == ch) { len = strlen(huffmanCode[i].code); for (j = 0; j < len; ++j) code[j] = huffmanCode[i].code[j]; fwrite(code, sizeof(char), len, fpOut); break; } } } fclose(fpIn); fclose(fpOut); printf("压缩成功!\n"); } // 解压文件 void decompressFile(char *fileName) { FILE *fpIn, *fpOut; struct HuffmanCode huffmanCode[256]; char ch, *code; int i, j, len, size; fpIn = fopen(fileName, "rb"); if (fpIn == NULL) { printf("打开文件失败!\n"); return; } fpOut = fopen("decompressed.txt", "w"); fread(&size, sizeof(int), 1, fpIn); for (i = 0; i < size; ++i) { fread(&huffmanCode[i].data, sizeof(char), 1, fpIn); fread(&len, sizeof(int), 1, fpIn); huffmanCode[i].code = (char*)malloc((len + 1) * sizeof(char)); fread(huffmanCode[i].code, sizeof(char), len, fpIn); huffmanCode[i].code[len] = '\0'; } code = (char*)malloc(MAX_TREE_HT * sizeof(char)); struct MinHeapNode *root = buildHuffmanTree(NULL, NULL, 0); struct MinHeapNode *curr = root; while ((ch = fgetc(fpIn)) != EOF) { for (i = 0; i < 8; ++i) { if (ch & (1 << (7 - i))) // ASCII码从高位开始读 curr = curr->right; else curr = curr->left; if (isLeaf(curr)) { fputc(curr->data, fpOut); curr = root; } } } fclose(fpIn); fclose(fpOut); printf("解压成功!\n"); } int main() { char data[] = {'a', 'b', 'c', 'd', 'e', 'f'}; int freq[] = {5, 9, 12, 13, 16, 45}; int size = sizeof(data) / sizeof(data[0]); struct HuffmanCode huffmanCode[256]; struct MinHeapNode* root = buildHuffmanTree(data, freq, size); char code[MAX_TREE_HT]; int top = 0; generateCodes(root, code, top, huffmanCode); printHuffmanCodes(huffmanCode, size); compressFile("input.txt", huffmanCode, size); decompressFile("compressed.dat"); return 0; } ``` 上述代码中,我们使用了最小优先队列来构建Huffman树。在生成Huffman编码时,我们使用了递归的方法遍历树,并记录下每个叶子节点的编码。在压缩文件时,我们将字符的Huffman编码写入输出文件中。在解压文件时,我们读取压缩文件中的编码信息和字符频率表,根据它们构建Huffman树,并根据编码信息还原原始字符串。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值