#include <iostream>
#include <iomanip>
#include <string>
#include <cstdlib>
using namespace std; //定义哈夫曼树存储结构
typedef struct
{
char data; //存放结点数据
int weight; //记录结点权值
int parent, lchild, rchild;
}HTNode, * HuffmanTree; //哈夫曼编码存储表示
typedef char** HuffmanCode; //动态分配数组储存哈夫曼编码表 //初始化
HuffmanTree InitHuffman(HuffmanTree& HT, int n)
{
if (n < )
{
cout << "输入初态结点数目不正确!!!" << endl;
return NULL;
}
else
{
int m = * n - ; //n个叶子结点的哈夫曼树有2n-1个结点
HT = new HTNode[m + ]; //0号单元未用,申请m+1个空间
for (int i = ; i <= m; i++)
{
HT[i].parent = ;
HT[i].lchild = ;
HT[i].rchild = ;
HT[i].weight = ;
HT[i].data = '-';
} //输入初始数据,为了方便理解HT[0]不存放数据
cout << "请输入初态结点数据及相应权值(格式形式:a 3):";
for (int i = ; i <= n; i++)
{
cin >> HT[i].data;
cin >> HT[i].weight;
}
return HT;
}
} //打印HT
void PrintState(HuffmanTree& HT, int n)
{
int m = * n - ; //总结点数
cout << "结点i\tdata值\tweight\tparent\tlchild\trchild" << endl;
for (int i = ; i <= m; i++)
{
cout << setw() << i << "\t";
cout << setw() << HT[i].data << "\t";
cout << setw() << HT[i].weight << "\t";
cout << setw() << HT[i].parent << "\t";
cout << setw() << HT[i].lchild << "\t";
cout << setw() << HT[i].rchild << "\t" << endl;
}
} //选择双亲域为0且两权值最小的结点,选择范围为1到i-1
int* Select(HuffmanTree& HT, int n, int* Idx)
{
int MIN, MinSecond;
//打擂台法使权最小值和次权最小值最大(假设第一个值权值最小无法进行)
MIN = MinSecond = ;
//循环从1到n次
for (int i = ; i <= n; i++)
{
//如果双亲为0(未删除结点与新生成结点),一定会执行if语句,寻找权最小值
if ((HT[i].parent == ) && HT[i].weight < MIN)
{
//将最小的权值给MinSecond方便寻找次最小值
MinSecond = MIN;
MIN = HT[i].weight;
//记录权最小值下标
Idx[] = i;
}
//否则如果满足条件寻找次最小值
else if ((HT[i].parent == ) && HT[i].weight < MinSecond)
{
MinSecond = HT[i].weight;
//记录权次最小值下标
Idx[] = i;
} }
return Idx;
} //构造哈夫曼树
void CreateHuffmanTree(HuffmanTree& HT, int n)
{
if (n <= )
return; int m = * n - ; //n个叶子结点的哈夫曼树有2n-1个结点
//从n+1开始构造哈弗曼树
for (int i = n + ; i <= m; i++)
{
//Idx用于存放两个返回最小权值的下标,i-1为前n个结点,后面随着i增加,n也增加
int* Idx = Idx = new int[];
Idx = Select(HT, i - , Idx);
int s1 = Idx[]; //权最小值下标
int s2 = Idx[]; //权次最小值下标
//给两权值最小的结点置双亲,使s1,s2结点不在录入Select范围
HT[s1].parent = i;
HT[s2].parent = i; //给新节点i左右孩子置位s1,s2
HT[i].lchild = s1;
HT[i].rchild = s2; //给新结点赋权值
HT[i].weight = HT[s1].weight + HT[s2].weight;
delete[]Idx;
}
} //哈夫曼编码
void CreateHuffmanCode(HuffmanTree& HT, HuffmanCode& HC, int n)
{
HC = new char* [n + ]; //分配字符的空间
char* TempCode = new char[n]; //分配临时编码表空间n个
TempCode[n - ] = '\0'; //编码有叶子结点开始逆序存放,先将末尾结束符标志打上,为后序strcpy
//for循环逐个逆序求叶子结点的哈夫曼编码
for (int i = ; i <= n; i++)
{
int start = n - ; //开始存放的起点指向末尾,用于后面HC拷贝是的起始地址
int NodeIdx = i; //NodeIdx最开始存放叶子结点序号
int ParentsIdx = HT[i].parent; //Parents指向其双亲结点序号
//若有双亲则由下到上进行编码,编码的字符,从序号1开始
while (ParentsIdx)
{
start--; //在一维数组末尾,strat先指向最后
//若双亲结点的左结点序号为NodeIdx,将0打入一维临时数组中,否则打入1
if (HT[ParentsIdx].lchild == NodeIdx)
TempCode[start] = '';
else
TempCode[start] = '';
//结点的序号更新,进入上一层
NodeIdx = ParentsIdx;
ParentsIdx = HT[NodeIdx].parent;
}
//临时一维数组存放编码成功,开始用HC顺序存放字符编码
HC[i] = new char[n - start]; //为序号为i的字符分配编码空间
strcpy_s(HC[i], n - start, &TempCode[start]); //编码字符串拷贝strcpy报错因为没有指定长度
}
//打印哈夫曼编码
cout << "---字符对应编码----" << endl;
for (int i = ; i <= n; i++)
{
cout << HT[i].data << "\t\t" << HC[i] << endl;
}
delete []TempCode;
} //输入字符打印哈夫曼编码
void PrintCode(HuffmanTree & HT, HuffmanCode & HC, int n)
{
char *str =new char[]; //存储需要解码的字符
int flag = ; //flag用于检查输入是否错误
cout << "请输入需要编码的字符:";
cin >> str;
//匹配字符并打印相应的哈夫曼编码
cout << "输入的字符哈夫曼编码为:";
for (int j = ; j < strlen(str); j++)
{
flag = ;
for (int i = ; i <= n; i++)
{
//匹配成功打印编码
if (HT[i].data == str[j])
{
cout << HC[i] ;
flag = ; //匹配成功
}
}
//如果有匹配失败的情况,则跳出循环
if (flag == )
{
cout << "\n第" << j+ << "字符输入错误!" ;
break;
} }
putchar();
delete []str; //释放str空间
} //哈夫曼解码并打印
void HuffmanDecode(HuffmanTree& HT, int n)
{
char* str = new char[];
cout << "请输入二进制编码:";
cin >> str; int flag = ; //用于检查二进制编码是否输入错误
cout << "解码对应字符为:";
//遍历二进制编码
for (int i = ; i < strlen(str);)
{
int Root = * n - ; //Root为根结点序号
//当结点的左右孩子不为空时进入循环,由根结点进入
while (HT[Root].lchild && HT[Root].rchild)
{
if (str[i] == '')
Root = HT[Root].lchild;
else if (str[i] == '')
Root = HT[Root].rchild;
else
{
cout << "输入的二级制编码有误!" << endl;
flag = ;
break;
}
i++; //相后读取二进制编码,i值更新
}
//如果找到错误跳出循环
if (flag)
break;
//打印编码对应字符
cout << HT[Root].data;
}
delete []str;
} int main()
{
int n;
cout << "请输入哈夫曼树初态结点数:";
cin >> n; //初始化哈夫曼树
HuffmanTree HT = InitHuffman(HT, n);
//打印HT初态
cout << "--------------------HT初态--------------------" << endl;
PrintState(HT, n);
//构造哈夫曼树
CreateHuffmanTree(HT, n);
//打印HT终态
cout << "--------------------HT终态--------------------" << endl;
PrintState(HT, n); HuffmanCode HC;
//哈夫曼编码-由下而上
CreateHuffmanCode(HT, HC, n);
//打印字符对应编码
PrintCode(HT, HC, n);
//哈夫曼解码并打印-由上而下
HuffmanDecode(HT, n); return ;
}

//由于编译器版本原因strcpy出现不安全原因,导致无法运行,后使用strcpy_s给予拷贝长度得到解决;把“==”写成“=”导致报错;
/*
输入字符串统计字符个数(权值)
int CreateWeightArray(char* str, int* Array) {
//初始化权值数组,128为str[i]的最大数值
for (int i = 0; i < 128; i++)
{
Array[i] = 0;
}

int length = 0;

//利用下标记录位权
for (int i = 0; str[i]; i++)
{
Array[str[i]]++; //值加1,下标即字符
}
//统计字符串去重后的长度
for (int i = 0; i < 128; i++)
{
if (Array[i] != 0)
{
length++;
}
}
return length;
}
*/

c++实现哈夫曼树,哈夫曼编码,哈夫曼解码(字符串去重,并统计频率)的更多相关文章

  1. 20172332 2017-2018-2 《程序设计与数据结构》Java哈夫曼编码实验--哈夫曼树的建立,编码与解码

    20172332 2017-2018-2 <程序设计与数据结构>Java哈夫曼编码实验--哈夫曼树的建立,编码与解码 哈夫曼树 1.路径和路径长度 在一棵树中,从一个结点往下可以达到的孩子 ...

  2. [C++]哈夫曼树(最优满二叉树) / 哈夫曼编码(贪心算法)

    一 哈夫曼树 1.1 基本概念 算法思想 贪心算法(以局部最优,谋求全局最优) 适用范围 1 [(约束)可行]:它必须满足问题的约束 2 [局部最优]它是当前步骤中所有可行选择中最佳的局部选择 3 [ ...

  3. 采用C++实现哈夫曼树的创建并输出哈夫曼编码

    一.这篇随笔来自一道信息论的作业题,因为手动编码过于复杂,所以想到了编程解决,原题目如下图所示: 二.源代码如下: #include <iostream> #include <str ...

  4. 哈夫曼树【最优二叉树】【Huffman】

    [转载]只为让价值共享,如有侵权敬请见谅! 一.哈夫曼树的概念和定义 什么是哈夫曼树? 让我们先举一个例子. 判定树:         在很多问题的处理过程中,需要进行大量的条件判断,这些判断结构的设 ...

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

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

  6. C++哈夫曼树编码和译码的实现

    一.背景介绍: 给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree).哈夫曼树是带权路径长度最短的树,权值较大的 ...

  7. 哈夫曼树(三)之 Java详解

    前面分别通过C和C++实现了哈夫曼树,本章给出哈夫曼树的java版本. 目录 1. 哈夫曼树的介绍 2. 哈夫曼树的图文解析 3. 哈夫曼树的基本操作 4. 哈夫曼树的完整源码 转载请注明出处:htt ...

  8. 哈夫曼树(二)之 C++详解

    上一章介绍了哈夫曼树的基本概念,并通过C语言实现了哈夫曼树.本章是哈夫曼树的C++实现. 目录 1. 哈夫曼树的介绍 2. 哈夫曼树的图文解析 3. 哈夫曼树的基本操作 4. 哈夫曼树的完整源码 转载 ...

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

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

  10. 【算法】赫夫曼树(Huffman)的构建和应用(编码、译码)

    参考资料 <算法(java)>                           — — Robert Sedgewick, Kevin Wayne <数据结构>       ...

随机推荐

  1. DevExpress Winform使用单例运行程序方法和非DevExpress使用Mutex实现程序单实例运行且运行则激活窗体的方法

    原文:DevExpress Winform使用单例运行程序方法和非DevExpress使用Mutex实现程序单实例运行且运行则激活窗体的方法 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA ...

  2. C#设计模式:单例模式(Singleton)

    一,单例模式:它的主要特点不是根据客户程序调用生成一个新的实例,而是控制某个类型的实例数量-唯一一个,就是保证在整个应用程序的生命周期中,在任何时刻,被指定的类只有一个实例,并为客户程序提供一个获取该 ...

  3. 《Redis深度历险:核心原理和应用实践》学习笔记一

    1.redis五种数据结构 1.1 String字符串类型,对应java字符串类型 用户信息序列化后,可以用string类型存入redis中批量读写string类型,见效网络消耗数字类型的string ...

  4. find命令使用详解

    一.主要内容 ====================================== 1. 用文件名查找文件 2.用文件名查找文件,忽略大小写 3. 使用mindepth和maxdepth限定搜 ...

  5. jenkins持续集成(一): 在Linux下的安装与配置

    撸了今年阿里.网易和美团的面试,我有一个重要发现.......>>> 前提: 安装了JDK,并配置好环境变量:JAVA_HOME 安装了Maven,并配置好环境变量:MAVEN_HO ...

  6. 【串线篇】Mybatis拓展之MBG

    MBG-逆向工程 一.介绍 MBG:MyBatis Generator:代码生成器: MyBatis官方提供的代码生成器:帮我们逆向生成: 正向: table----javaBean---BookDa ...

  7. freertos优秀博客收藏

    https://blog.csdn.net/zhzht19861011 朱工的专栏 专注/深入/分享 https://blog.csdn.net/xukai871105 xukai871105 专注于 ...

  8. Linux中的bin文件夹

    ~/bin适合放个人用户的 script /usr/local/bin存放系统中所有用户都可以使用的 script /usr/local/sbin存放管理员的 script /usr/local/目录 ...

  9. C#基础提升系列——C#文件和流

    C#文件和流 本文主要是对C#中的流进行详细讲解,关于C#中的文件操作,考虑到后期.net core跨平台,相关操作可能会发生很大变化,所以此处不对文件系统(包括目录.文件)过多的讲解,只会描述出在. ...

  10. 比传统事务快10倍?一张图读懂阿里云全局事务服务GTS

    近日,阿里云全局事务服务GTS正式上线,为微服务架构中的分布式事务提供一站式解决方案.GTS的原理是将分布式事务与具体业务分离,在平台层面开发通用的事务中间件GTS,由事务中间件协调各服务的调用一致性 ...