一、 问题

需求分析

此图转载于账号u014447560(深海鲸歌)

代码分析

结构体定义使用

需要定义树的结构,打印树的结构时需要用的队列

typedef struct node{
char data;
int weight;
int parent;
int lchild,rchild;
int mark;
}huffmanTree; typedef struct code_N{
char ch;
char Bi_code[100];
}Char_coding; typedef struct queue_node{
int data;
struct LinkNode *next;
}LinkNode; typedef struct queue{
struct queue_node* rear,*front;
}LinkQueue;
int allchar =0; //记录所有字母的个数

建立哈夫曼树,首先需要找到两个权值最小的两个叶子结点,然后建树

初始化树


void find_min(huffmanTree *tree,int max,int *firstMin,int *secMin){
//查找两个最小的值
int smallindex[2];
for(int j = 0;j<2;j++){
int temp = 9999;
int count = 0;
int final_index =0;
for(int k = 0;k<=max;k++) {
if(tree[k].mark==0){
if(tree[k].weight<temp){
final_index = k;
temp = tree[k].weight;
}
}
else{
count++;
}
// 最后一个结点
if(count ==max &&j ==0){
return ;
} }
tree[final_index].mark =1;
smallindex[j] = final_index;
}
*firstMin = smallindex[0];
*secMin =smallindex[1]; }
//建树
huffmanTree * Init_huffmanTree(huffmanTree *tree,char *ch,int *weight){ int all_node;
int count = strlen(ch);
if(strlen(ch)<0){
return NULL;
}
all_node = 2*count -1;
// 初始化哈夫曼树
tree = (huffmanTree*) malloc((all_node)*sizeof (huffmanTree));
for(int i = 0;i<count;i++){
tree[i].data =ch[i];
tree[i].weight = weight[i];
tree[i].parent =-1;
tree[i].lchild=-1;
tree[i].rchild =-1;
tree[i].mark =0;
}
for(int j = count;j<all_node;j++){
tree[j].data ='#';
tree[j].parent =-1;
tree[j].weight =-1;
tree[j].parent =tree[j].rchild=tree[j].lchild =-1;
tree[j].mark =0;
}
for(int i = count;i<all_node;i++){
int fristMin,secMin;
fristMin = secMin = 0;
find_min(tree,i-1,&fristMin,&secMin); //找到最小索引的两个的下标
tree[fristMin].parent = i;
tree[secMin].parent = i;
tree[i].weight = tree[fristMin].weight+tree[secMin].weight;
tree[i].lchild = fristMin;
tree[i].rchild = secMin;
} return tree; //返回初始化的哈夫曼树
}

哈夫曼编码(我采用的是从叶子结点–>根节点,所以实际是反过来的)

Char_coding * print_huffmancode(huffmanTree *tree,Char_coding *coding){
// 编码
int ch_length = allchar; //总数
coding = (Char_coding*) malloc(sizeof (Char_coding)*(ch_length));
Char_coding s;
for (int i = 0;i<ch_length;i++) {
int child_curr = i;
int parent_curr = tree[i].parent;
int j = 0; //记录字符的索引
int length_coding =0; //记录编码的长度,在最后设置\000
while (parent_curr != -1) {
if (tree[parent_curr].lchild == child_curr) {
// 左子树设为0
s.Bi_code[j] = '0';
child_curr = parent_curr;
parent_curr = tree[child_curr].parent;
length_coding++;
j++;
} else {
// 右子树设为1
s.Bi_code[j] = '1';
child_curr = parent_curr;
parent_curr = tree[child_curr].parent;
length_coding++;
j++;
}
}
s.ch = tree[i].data;
coding[i] = s;
// 处理串的末尾
coding[i].Bi_code[length_coding] ='\000';
// printf("%d %s\n",i, coding[i].Bi_code);
}
return coding; //返回编码好的哈夫曼编码,从叶子到根节点
}

使用哈夫曼树译码(包括文件保存)

void Decoding(Char_coding *coding,huffmanTree *tree,char *f_name,char *fp_write){
// 译码
FILE *fp1;
FILE *fp_w;
int length = allchar;
fp_w = fopen(fp_write,"w+");
fp1= fopen(f_name,"r");
if(fp1 ==NULL){
printf("译码文件不存在");
}
int root_index = 2*length-2;
int child_root = root_index;
while (!feof(fp1)){
char temp = fgetc(fp1);
if(temp!='\n'){
if(temp == '0'){
child_root = tree[child_root].lchild;
// 找到叶子结点
if(tree[child_root].lchild ==-1 &&tree[child_root].rchild ==-1){
// printf("%c",tree[child_root].data);
fputc(tree[child_root].data,fp_w);
// 回溯到根节点
child_root = root_index;
}
}
else {
if (tree[child_root].rchild != -1) {
child_root = tree[child_root].rchild;
if (tree[child_root].lchild == -1 && tree[child_root].rchild == -1) {
// printf("%c", tree[child_root].data);
fputc(tree[child_root].data,fp_w);
child_root = root_index;
}
}
}
}
else{
continue;
}
}
fclose(fp1);
fclose(fp_w);
}

全部代码(测试文件之类的在下面的链接)

#include <stdio.h>
#include <string.h>
#include <malloc.h> typedef struct node{
char data;
int weight;
int parent;
int lchild,rchild;
int mark;
}huffmanTree; typedef struct code_N{
char ch;
char Bi_code[100];
}Char_coding; typedef struct queue_node{
int data;
struct LinkNode *next;
}LinkNode; typedef struct queue{
struct queue_node* rear,*front;
}LinkQueue; int allchar =0; //记录所有字母的个数 void find_min(huffmanTree *tree,int max,int *firstMin,int *secMin){
//查找两个最小的值
int smallindex[2];
for(int j = 0;j<2;j++){
int temp = 9999;
int count = 0;
int final_index =0;
for(int k = 0;k<=max;k++) {
if(tree[k].mark==0){
if(tree[k].weight<temp){
final_index = k;
temp = tree[k].weight;
}
}
else{
count++;
}
// 最后一个结点
if(count ==max &&j ==0){
return ;
} }
tree[final_index].mark =1;
smallindex[j] = final_index;
}
*firstMin = smallindex[0];
*secMin =smallindex[1]; } Char_coding * print_huffmancode(huffmanTree *tree,Char_coding *coding){
// 编码
int ch_length = allchar; //总数
coding = (Char_coding*) malloc(sizeof (Char_coding)*(ch_length));
Char_coding s;
for (int i = 0;i<ch_length;i++) {
int child_curr = i;
int parent_curr = tree[i].parent;
int j = 0; //记录字符的索引
int length_coding =0; //记录编码的长度,在最后设置\000
while (parent_curr != -1) {
if (tree[parent_curr].lchild == child_curr) {
// 左子树设为0
s.Bi_code[j] = '0';
child_curr = parent_curr;
parent_curr = tree[child_curr].parent;
length_coding++;
j++;
} else {
// 右子树设为1
s.Bi_code[j] = '1';
child_curr = parent_curr;
parent_curr = tree[child_curr].parent;
length_coding++;
j++;
}
}
s.ch = tree[i].data;
coding[i] = s;
// 处理串的末尾
coding[i].Bi_code[length_coding] ='\000';
// printf("%d %s\n",i, coding[i].Bi_code);
}
return coding; //返回编码好的哈夫曼编码,从叶子到根节点
} huffmanTree * Init_huffmanTree(huffmanTree *tree,char *ch,int *weight){ int all_node;
int count = strlen(ch);
if(strlen(ch)<0){
return NULL;
}
all_node = 2*count -1;
// 初始化哈夫曼树
tree = (huffmanTree*) malloc((all_node)*sizeof (huffmanTree));
for(int i = 0;i<count;i++){
tree[i].data =ch[i];
tree[i].weight = weight[i];
tree[i].parent =-1;
tree[i].lchild=-1;
tree[i].rchild =-1;
tree[i].mark =0;
}
for(int j = count;j<all_node;j++){
tree[j].data ='#';
tree[j].parent =-1;
tree[j].weight =-1;
tree[j].parent =tree[j].rchild=tree[j].lchild =-1;
tree[j].mark =0;
}
for(int i = count;i<all_node;i++){
int fristMin,secMin;
fristMin = secMin = 0;
find_min(tree,i-1,&fristMin,&secMin); //找到最小索引的两个的下标
tree[fristMin].parent = i;
tree[secMin].parent = i;
tree[i].weight = tree[fristMin].weight+tree[secMin].weight;
tree[i].lchild = fristMin;
tree[i].rchild = secMin;
} return tree; //返回初始化的哈夫曼树
} huffmanTree * Init_file_tree(huffmanTree *tree,char* fp_save){ FILE *fp;
fp = fopen(fp_save,"r");
if(fp ==NULL){
printf("初始化文件不存在:%s",fp_save);
exit(0);
}
int count = 0;
while(!feof(fp)){
char temp[200];
fgets(temp,200,fp);
count++;
}
// 根据文件进行初始化
fclose(fp);
tree = (huffmanTree *) malloc(sizeof (huffmanTree)*(count-1));
fp = fopen(fp_save,"r");
for(int i = 0;i<count-1;i++){
fscanf(fp,"%c,%d,%d,%d,%d,%d\n",&tree[i].data,&tree[i].weight,
&tree[i].parent,&tree[i].lchild,&tree[i].rchild,&tree[i].mark);
}
allchar = (count+1)/2;
// printf("%c",tree[0].data);
return tree; //返回初始化的哈夫曼树
} void set_tree(huffmanTree *tree,char *file_save){
int length = allchar;
int node_len = 2*length -1;
FILE *fp_save;
fp_save = fopen(file_save,"w+");
if (fp_save==NULL){
printf("文件打开错误");
exit(0);
}
for(int i = 0; i<node_len;i++){
fprintf(fp_save,"%c,%d,%d,%d,%d,%d\n",tree[i].data,tree[i].weight,tree[i].parent,tree[i].lchild,tree[i].rchild,tree[i].mark);
// printf("%c %d %d %d %d\n",tree[i].data,tree[i].parent,tree[i].lchild,tree[i].rchild,tree[i].mark);
}
fclose(fp_save);
} void seting_file(Char_coding *coding,huffmanTree *tree,char *word,char *f_name){
// 保存目标文本编码到文件
int length = allchar;
int char_length = strlen(word);
FILE *fp;
fp = fopen(f_name,"w+");
if(fp==NULL){
printf("文件打开错误");
exit(0);
}
unsigned long count_line = 0;
for(int i = 0;i<char_length;i++){
for(int j = 0;j<length;j++){
if(coding[j].ch == word[i]){
for(int k =strlen(coding[j].Bi_code)-1;k>=0;k--){
// printf("%c",coding[j].Bi_code[k]);
fputc(coding[j].Bi_code[k],fp);
// 在写入文件时50个换行
// if((count_line+1)%50==0){
// fputc('\n',fp);
// }
count_line++;
}
break;
}
}
}
fclose(fp);
} void Decoding(Char_coding *coding,huffmanTree *tree,char *f_name,char *fp_write){
// 译码
FILE *fp1;
FILE *fp_w;
int length = allchar;
fp_w = fopen(fp_write,"w+");
fp1= fopen(f_name,"r");
if(fp1 ==NULL){
printf("译码文件不存在");
}
int root_index = 2*length-2;
int child_root = root_index;
while (!feof(fp1)){
char temp = fgetc(fp1);
if(temp!='\n'){
if(temp == '0'){
child_root = tree[child_root].lchild;
// 找到叶子结点
if(tree[child_root].lchild ==-1 &&tree[child_root].rchild ==-1){
// printf("%c",tree[child_root].data);
fputc(tree[child_root].data,fp_w);
// 回溯到根节点
child_root = root_index;
}
}
else {
if (tree[child_root].rchild != -1) {
child_root = tree[child_root].rchild;
if (tree[child_root].lchild == -1 && tree[child_root].rchild == -1) {
// printf("%c", tree[child_root].data);
fputc(tree[child_root].data,fp_w);
child_root = root_index;
}
}
}
}
else{
continue;
}
}
fclose(fp1);
fclose(fp_w);
} //入队
void InQueue(LinkQueue *queue,int x){
//生成新节点
LinkNode *p = (LinkNode*) malloc(sizeof (LinkNode*));
p->data = x;
p->next = NULL;
if(queue->front ==NULL){
queue->front = queue->rear = p;
}
else {
queue->rear->next = p;
queue->rear = p;
}
}
//出队列
int DeQueue(LinkQueue *queue){
if(queue->front ==NULL){
return 0;
}
LinkNode *p = queue->front;
int temp = p->data; //保存出队列的值
queue->front = p->next;
// free(p);
return temp;
} void init_queue(LinkQueue *queue){
queue->front = queue->rear =NULL;
} void traverse_tree(huffmanTree*tree){
// 层次遍历
LinkQueue queue;
init_queue(&queue);
int length = allchar;
int all_node = 2*length-1;
int count = all_node;
int root_index = all_node-1;
int ceng_index = root_index;
int cennum =1;
printf("下面是按照凹入表示法打印的哈夫曼树");
while(count>0){
for(int i = 0;i<cennum;i++){
printf("-");
}
printf("%c\n",tree[root_index].data);
count--;
int l_child_index = tree[root_index].lchild;
int right_index = tree[root_index].rchild;
if(l_child_index!=-1){
InQueue(&queue,l_child_index);
}
if(ceng_index == root_index){
cennum++;
// printf("\n");
if(l_child_index !=-1 &&right_index !=-1 ){
ceng_index = right_index;
}
else if(l_child_index ==-1 && right_index !=-1){
ceng_index = right_index;
}
else if(l_child_index !=-1&& right_index ==-1){
ceng_index = l_child_index;
}
else{
ceng_index = tree[tree[tree[root_index].parent].lchild].rchild;
} }
if(right_index!=-1){
InQueue(&queue,right_index); }
root_index = DeQueue(&queue);
} } void showface() {
printf("\n\n");
printf("\t\t\t====================哈夫曼树译码系统====================\n");
printf("\t\t\t* *\n");
printf("\t\t\t* 1>.初始化哈夫曼树 \n");
printf("\t\t\t* 2>. 编码\n");
printf("\t\t\t* 3>. 译码\n");
printf("\t\t\t* 4>. 打印字符编码文件信息\n");
printf("\t\t\t* 5>. 打印哈夫曼树\n");
printf("\t\t\t* 0>. 退出本系统\n");
printf("\t\t\t* 欢迎使用本系统!\n\n");
printf("\t\t\t=========================================================\n");
} huffmanTree * input_data(huffmanTree *tree){
int num;
printf("请输入字符总数\n");
scanf("%d",&num);
allchar = num;
int weight[27] = {186, 64, 13, 22, 32, 103, 21,15, 47, 57 ,1 ,5, 32,20 ,57, 63, 15, 1 ,48 ,51, 80, 23 ,8, 18 ,1, 16, 1};
char ch[27][2] = {' ','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
tree= Init_huffmanTree(&tree,ch,&weight); //初始化哈夫曼树
return tree;
} void printf_code_print(char *filename){
FILE *fp;
fp = fopen(filename,"r");
if(fp==NULL){
printf("文件不存在");
exit(0);
}
int count=0;
while(!feof(fp)){
char temp = fgetc(fp);
if(temp !=EOF){
printf("%c",temp);
} if((count+1)%50 ==0){
printf("\n");
}
count++;
}
} int main() {
int button;
huffmanTree *tree;
char word[200];
int num;
Char_coding *coding;
int isinit = 0; char fp_save[200] ="../save_tree.txt";
char fp [200]="../codeing.txt";
char fp_w [200] = "../textfile.txt";
do {
showface();
printf("请输入相应的数字,进行相应的操作:\n");
scanf("%d", &button);
switch (button) {
case 1:
tree = input_data(&tree);
set_tree(tree,fp_save);
isinit =1;
break; case 2:
if(isinit ==0){
isinit =1;
tree = Init_file_tree(&tree,fp_save);
}
printf("请输入你想要编码的字符串\n");
scanf("%s",word);
coding =print_huffmancode(tree,&coding);
seting_file(coding,tree,word,fp);
break; case 3:
if(isinit ==0){
isinit =1;
tree = Init_file_tree(&tree,fp_save);
}
Decoding(coding,tree,fp,fp_w); break; case 4:
if(isinit ==0){
isinit =1;
tree = Init_file_tree(&tree,fp_save);
}
printf_code_print(fp);
break; case 5:
if(isinit ==0){
isinit =1;
tree = Init_file_tree(&tree,fp_save);
}
traverse_tree(tree);
break;
case 0:
printf("即将退出.....");
exit(0); default:
printf("您输入的指令不正确,请重新输入");
break;
}
printf("\n\n");
} while (button); return 0;
}

源码及文件

链接:https://pan.baidu.com/s/154ub53uLrhoXN4IYwn5gUw
提取码:1314

总结

上述是哈夫曼树的编码和译码过程,本文的字母是自己已经固定了,但是基本不变,本人是菜鸡一枚,需要的可以自取,如果有问题,可以在评论区交流,或者私信我。

数据结构哈夫曼树(C语言版)的更多相关文章

  1. 数据结构-哈夫曼树(python实现)

    好,前面我们介绍了一般二叉树.完全二叉树.满二叉树,这篇文章呢,我们要介绍的是哈夫曼树. 哈夫曼树也叫最优二叉树,与哈夫曼树相关的概念还有哈夫曼编码,这两者其实是相同的.哈夫曼编码是哈夫曼在1952年 ...

  2. C#数据结构-赫夫曼树

    什么是赫夫曼树? 赫夫曼树(Huffman Tree)是指给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小.哈夫曼树(也称为最优二叉树)是带权路径长度最短的树,权值较大的结点 ...

  3. Java数据结构和算法(四)赫夫曼树

    Java数据结构和算法(四)赫夫曼树 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 赫夫曼树又称为最优二叉树,赫夫曼树的一个 ...

  4. 6-9-哈夫曼树(HuffmanTree)-树和二叉树-第6章-《数据结构》课本源码-严蔚敏吴伟民版

    课本源码部分 第6章  树和二叉树 - 哈夫曼树(HuffmanTree) ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接☛☛☛ <数据结构-C语言版> ...

  5. Android版数据结构与算法(七):赫夫曼树

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 近期忙着新版本的开发,此外正在回顾C语言,大部分时间没放在数据结构与算法的整理上,所以更新有点慢了,不过既然写了就肯定尽力将这部分完全整理好分享出 ...

  6. 数据结构之C语言实现哈夫曼树

    1.基本概念 a.路径和路径长度 若在一棵树中存在着一个结点序列 k1,k2,……,kj, 使得 ki是ki+1 的双亲(1<=i<j),则称此结点序列是从 k1 到 kj 的路径. 从 ...

  7. C语言数据结构之哈夫曼树及哈夫曼编码的实现

    代码清单如下: #pragma once #include<stdio.h> #include"stdlib.h" #include <string.h> ...

  8. 哈夫曼树(一)之 C语言详解

    本章介绍哈夫曼树.和以往一样,本文会先对哈夫曼树的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现:实现的语言虽不同,但是原理如出一辙,选择其中之一进行了解即可.若 ...

  9. 数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  10. 【数据结构】赫夫曼树的实现和模拟压缩(C++)

    赫夫曼(Huffman)树,由发明它的人物命名,又称最优树,是一类带权路径最短的二叉树,主要用于数据压缩传输. 赫夫曼树的构造过程相对比较简单,要理解赫夫曼数,要先了解赫夫曼编码. 对一组出现频率不同 ...

随机推荐

  1. .NET实验三

    实验名称:实验三 Windows 应用程序开发 一. 实验目的 1. 掌握窗口控件的使用方法: 2. 掌握 Windows 的编程基础. 二. 实验要求   根据要求,编写 C#程序,并将程序代码和运 ...

  2. 第二章 excel的快捷键操作

    本章介绍excel中部分常用的快捷键 1.文件类 工作簿操作:Ctrl + N 新建:Ctrl + w 关闭当前:Ctrl + S 保存 F12 当前另存为 Ctrl + p 打印当前 2.通用类 C ...

  3. 115、商城业务---分布式事务---使用Springboot提供的Seata解决分布式事务

    https://seata.io/zh-cn/ seata使用Seata AT模式控制分布式事务的步骤: 1.每一个想控制分布式事务的服务对应的数据库都需要创建一个UNDO_LOG 表 CREATE ...

  4. ssh双击互信

    默认公钥文件/root/.ssh/id_rsa.pub默认私钥文件/root/.ssh/id_rsa 只有将公钥文件文件拷到其他的服务器上才能登录别的服务器.   服务器A 192.168.1.133 ...

  5. TCP通信聊天服务端和客户端(C/C++语言开发)附完整源码

    距离上次学Python写的Python实现简单聊天室已经过去好久了,现在学c++又写了一遍,其实过程差不多,无非是语法的变化,目前仅实现最简单的一对一的通信,然后改就是了,接下来应该是多线程了,话不多 ...

  6. ArchKeeper (开篇):架构守护平台的问题与理念

    作者:京东科技 倪新明 在敏捷开发环境下,系统通过迭代增量的交付价值,系统架构也是如此.团队不可能在项目之初就建立完美的系统架构,系统架构应该随着系统迭代不断演进.架构演进和架构腐化是看待架构的不同视 ...

  7. 一文明白:JavaScript异步编程

    同步和异步 JS是单线程 JavaScript语言的一大特点是单线程,同一时间只能做一件事 (单线程的JS 就是一个傻子,脑子一根筋,做着当前的这件事情,没有完成之前,绝对不会做下一件事情) 当然,这 ...

  8. 写Java程序有感

    最近我练习了Java的学生管理系统的程序代码,多亏了前段时间小学期的系统类练习,让我比较容易地就能够理解该题的题意,再根据我学到的相关的Java知识,就好像是一个套用公式的逻辑. 用到的相关知识: 1 ...

  9. 141. Linked List Cycle (Easy)

    ps:能力有限,若有错误及纰漏欢迎指正.交流 Linked List Cycle (Easy) https://leetcode.cn/problems/linked-list-cycle/descr ...

  10. 使用python-gitlab获取本地gitlab仓库project信息的方法

    代码中有注释,直接看代码 #coding:utf8 #!/usr/bin/env python #@author: 9527 import gitlab import openpyxl import ...