64,690
社区成员
发帖
与我相关
我的任务
分享
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct hufftree hnode, *phtree;
struct hufftree{
int a;
phtree ltree;
phtree rtree;
};
void min_smin(const hnode *buffer,const int size, int *secondmin, int *min)
{
if(size <= 1)
return;
*min = buffer[0].a;
*secondmin = 101;
for(int i = 1;i < size;i++)
{
if(buffer[i].a <= *min)
{
*secondmin = *min;
*min = buffer[i].a;
}
else if (buffer[i].a < *secondmin)
*secondmin = buffer[i].a;
}
}
/*
这个霍夫曼的设计,就是先找到最小的两个节点,对于第一个我们仅仅是复制一个节点
对于第二小的数的节点,我们复制一个一样的节点,同时把复制的两个节点作为第二小
的原始节点的左右子树,同时把原始节点的大小修改为最小的两个节点值的和!
中间我们101作为访问过的节点的值,因为我们的霍夫曼树中每一个节点之后才是100,
也就是应该小于100,用101适合做标记。
同时我们使用一个last最为最后的一个节点的索引,因为最后的一个节点已经是最终的
树了,最后用tree参数复制最后的一个节点。
*/
void creat_huff(hnode buffer[],int size, phtree tree) {
int insize = size;
int smin, min;
int j = 0, count = 0;
int last = 0;
phtree node1, node2;
while(1 < insize) {
//求最小的两个数
min_smin(buffer, size, &smin, &min);
while(j < size) {
if(buffer[j].a == smin || buffer[j].a == min) {
if(count == 0) {
node1 = new hnode;
// make a copy of a min node
node1->a = buffer[j].a;
node1->ltree = buffer[j].ltree;
node1->rtree = buffer[j].rtree;
buffer[j].a = 101;
count++;
}
else if(count == 1){
node2 = new hnode;
// make a copy of a min node
node2->a = buffer[j].a;
node2->ltree = buffer[j].ltree;
node2->rtree = buffer[j].rtree;
//然后把node1和node2都附在buffer[j]上作为左右子树!
buffer[j].a = min+smin;
buffer[j].ltree = node1;
buffer[j].rtree = node2;
count = 0;
last = j;
break;
}
}
j++;
}
insize--, j = 0;
}
//复制最后一个节点
tree->a = buffer[last].a;
tree->ltree = buffer[last].ltree;
tree->rtree = buffer[last].rtree;
}
//用后序删除,因为这里用的引用传入参数,如果用前序或者中序的话
//释放了tree,那么他的左右子树不可知了,而且也不为0一般的情况下。
void freehuff(phtree &tree) {
if(tree) {
freehuff(tree->ltree);
freehuff(tree->rtree);
delete tree;
}
}
int main(){
hnode a[7];
for(int i = 0; i < 7; i++)
{
a[i].a = i+5;
a[i].ltree = a[i].rtree = NULL;
}
phtree newtree = new hnode;
creat_huff(a, 7, newtree);
freehuff(newtree);
return 0;
}
#include "Huffman.h"
#include "windows.h"
void show_info()
{
cout<<"==============================Huffman树显示程序=============================="<<endl;
cout<<"\t1.对文件进行编码\t\t2.对文件进行解码\t"<<endl;
cout<<"\t3.对指定字符进行编码\t\t4.退出程序"<<endl;
}
int get_cmd()
{
int i;
for(;;)
{
cout<<"请输入命令:";
cin>>i;
if (i<1||i>4)
cout<<"错误的命令!"<<endl;
else
break;
}
return i;
}
void exe_1();
void exe_2();
void exe_3();
int main()
{
int cmd;
for (;;)
{
show_info();
cmd = get_cmd();
if (cmd==4)
{
cout<<"谢谢使用!"<<endl;
Sleep(1000);
return 1;
}
switch (cmd)
{
case 1:
exe_1();break;
case 2:
exe_2();break;
case 3:
exe_3();break;
}
}
return 0;
}
void exe_1()
{
char filenames[256], filenamed[256];
cout<<"请输入文件名:";
cin>>filenames;
Huffman h;
h.LoadCharSet();
cout<<"数据装入中..."<<endl;
if (!h.CreateHuffmanTreeFromFile(filenames))
{
cout<<"装入文件出错,可能有未知的符!"<<endl;
return;
}
h.ShowCode();//显示编码
cout<<"请输入目标文件名:";
cin>>filenamed;
cout<<"数据写入中..."<<endl;
if (!h.CreateHuffmanTreeFromFile(filenames)||!h.EnCode(filenames,filenamed))
{
cout<<"文件编码出错!"<<endl;
return;
}
else
cout<<"文件编码成功!"<<endl;
cout<<"为你的编码文件输入一个编码对照文件名:";
cin>>filenamed;
if (!SaveAs(filenamed,h))
{
cout<<"写入文件出错!"<<endl;
return;
}
else
{
cout<<"编码对照文件写入文件成功!"<<endl;
return;
}
}
void exe_2()
{
char filename1[256], filename2[256];
cout<<"请输入编码对照文件名:";
cin>>filename1;
Huffman h;
if(!LoadFrom(filename1,h))
{
cout<<"装入编码对照文件出错!"<<endl;
return;
}
cout<<"请输入编码文件名:";
cin>>filename1;
cout<<"请输入目标文件名:";
cin>>filename2;
if(!h.DeCode(filename1,filename2))
{
cout<<"解码出错!"<<endl;
return;
}
else
{
cout<<"解码成功!"<<endl;
return;
}
}
void exe_3()
{
char c[128];
int w[128];
int n, i;
cout<<"请输入字符数:";
cin>>n;
if (n>128)
{
cout<<"字符数太多!"<<endl;
return;
}
for (i=0;i<n;i++)
{
cout<<"请输入第"<<i+1<<"个字符:"<<endl;
cin>>c[i];
}
for (i=0;i<n;i++)
{
cout<<"请输入第"<<i+1<<"个字符的权重:"<<endl;
cin>>w[i];
}
Huffman h;
h.LoadCharSet(c,n);
h.LoadWeight(w,n);
h.CreateHuffmanTreeFromData();
h.ShowCode();
}
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
#ifndef HUFFMAN_H
#define HUFFMAN_H
//下面的不符合标准,应该加上int
const N = 128;
const MAX_WEIGHT = 888888888;//最大权,如果不保险可以设为-1然后修改函数Huffman::select
const MAX_CHAR_LENGTH = 16;
typedef struct{
char HfmCode[MAX_CHAR_LENGTH];
char ch;
}code, *HuffmanCode;
typedef struct
{
unsigned int weight; /* 用来存放各个结点的权值*/
char ch;
unsigned int parent, LChild, RChild ; /*指向双亲、孩子结点的指针*/
}HTNode, * HuffmanTree; /*动态分配数组,存储哈夫曼树*/
/*类的使用说明:
1.定义一个对象;
2.如果是统计文件里所有的字符并对其进行编码的话
直接使用函数CreateHuffmanTreeFromFile从文件载
入,字符集为ASCII码,即0-127;
如果是对指定字符集进行编码的话,使用函数LoadCharSet
载入字符集,如果没有参数的话会自动载入ASCII码字符集
然后使用LoadWeight载入权重;
3.如果不是从文件载入则调用CreateHuffmanTreeFromData
生成Huffman树,并且生成相应编码,而从文件载入的话就会
自动生成Huffman树
4.EnCode对文件进行编码,如果遇到不认识的字符,则报错;
5.DeCode对文件进行解码,如果在EnCode中曾经跳过不认识的
字符,则不能成功解码,并且不会报错,后果不可预料;
6.ShowCode显示字符和对应编码
如果只是定义一个对象然后调用的话,后果不可预测;
7.WeightTest进行文件权重测试,载入一个文件,然后显示各
个字符的数目;
8.SaveAs可以把一个Huffman对象保存到一个文件;
9.LoadFrom可以从一个文件载入Huffman对象,但是不能进行
合法性判断,所以如果载入的是非SaveAs生成的对象,那么
后果很严重.
*/
class Huffman
{
int CHAR_NUM;//字符集的数目
int w[N+1]; /*各字符的权重*/
char st[N+1]; /* 字符集,和st[i]和s[i]对应,由于是只对字母进行编码,所以在 实际运行中没有起作用*/
HTNode ht[2*N];//树
code hc[N+1];//编码
bool CreateWeightFromFile(char*);//根据文件得到各字符的权重
void CreateHuffmanTree();
void select(int n, int *s1, int *s2);
void CreateHuffmanCode();//根据树求编码
int findcode(char ch);
public:
Huffman();
void LoadCharSet();//设置字符集为asc字符
void LoadCharSet(char*,int n);//设置字符集为指定字符
bool LoadWeight(int*,int);//装入数据
bool CreateHuffmanTreeFromData();//从本身数据创建H树
bool CreateHuffmanTreeFromFile(char*);//由一个文件创建H树
void ShowCode();//显示编码
bool EnCode(char*,char*);//根据当前数据,对文件进行编码
bool DeCode(char*,char*);//根据当前数据,对文件进行解码
void WeightTest(char*);//测试函数,调试中用的
};
Huffman::Huffman()
{
for (int i=1;i<=128;w[i++]=0);
}
void Huffman::LoadCharSet()//设置 字符集为asc字符集
{
for (int i=1;i<=128;hc[i].ch=st[i]=i-1,i++);
CHAR_NUM = 128;
}
void Huffman::LoadCharSet(char* c,int n)
{
for (int i=1;i<=n;hc[i].ch=st[i]=c[i-1],i++);
CHAR_NUM = n;
}
bool Huffman::CreateHuffmanTreeFromData()
{
CreateHuffmanTree();
CreateHuffmanCode();
return true;
}
bool Huffman::LoadWeight(int* wt,int n)
{
if (n!=CHAR_NUM)
return false;
for (int i=1;i<=n;w[i]=wt[i-1],i++);
return true;
}
bool Huffman::CreateWeightFromFile(char* fn)
{
fstream f;
f.open(fn,ios::in|ios::binary);
if (!f)
return false;
char t[2];
int i;
while (!f.eof())
{
f.read(t,1);
if (f.gcount()==1)
{
i = findcode(t[0]);
if (i!=0)
w[i]++;
else
return false;
}
}
f.close();
return true;
}
void Huffman::WeightTest(char* fn)
{
if (CreateWeightFromFile(fn))
for (int i=1;i<=CHAR_NUM;i++)
cout<<st[i]<<":\t"<<w[i]<<endl;
}
void Huffman::select(int n, int *s1, int *s2)
{ /*ht,为树所在数组的头指针,n为允许查找的最大序号,s1,s2,返回最小的两个序号*/
int p1=MAX_WEIGHT;
int p2=MAX_WEIGHT; /*p1, p2用来记录最小的两个权, 要求p1<p2*/
int pn1=0;
int pn2=0, i; /*pn1, pn2 用来记录这两个权的序号*/
for (i=1;i<=n;i++)
if (ht[i].weight<p1 && ht[i].parent==0)
{/*如果当前结点的权比p1小,那么p2的权赋为p1的权, p1的权为当前权,同时修改n1, pn2*/
/*查找的结点要求没有父结点*/
pn2=pn1;
p2=p1;
p1=ht[i].weight;
pn1=i;
}
else
if (ht[i].weight<p2 &&ht[i].parent==0)
{/*如果当前结点的权比p2小(由第一个if,当前权不比p1小),那么p2的权赋为当前权,同时修改pn2*/
p2=ht[i].weight;
pn2=i;
}
*s1=pn1;
*s2=pn2; /*赋值返回*/
}
void Huffman::CreateHuffmanTree()
{ /* w存放已知的n个权值,构造哈夫曼树ht */
/*由于是用数组的结构来存储树,故ht放的是数组的头指针*/
int m,i;
int s1, s2; /*在select函数中使用,用来存储最小权的结点的序号*/
m=2*CHAR_NUM-1;
for (i=1;i<=CHAR_NUM;i++)
{/*1-n号放叶子结点,初始化*/
ht[i].weight = w[i];
ht[i].ch=st[i];
ht[i].LChild = 0;
ht[i].parent = 0;
ht[i].RChild = 0;
}
for (i=CHAR_NUM+1;i<=m;i++)
{
ht[i].weight = 0;
ht[i].LChild = 0;
ht[i].parent = 0;
ht[i].RChild = 0;
} /*非叶子结点初始化*/
/* ------------初始化完毕!对应算法步骤1---------*/
for (i=CHAR_NUM+1;i<=m;i++) /*创建非叶子结点,建哈夫曼树*/
{ /*在(*ht)[1]~(*ht)[i-1]的范围内选择两个parent为0且weight最小的结点,其序号分别赋值给s1、s2返回*/
select(i-1,&s1,&s2);
ht[s1].parent=i;
ht[s2].parent=i; /*设置这两个最小的权的结点的父结点为i*/
ht[i].LChild=s1;
ht[i].RChild=s2; /*设置这个父结点的子结点*/
ht[i].weight=ht[s1].weight+ht[s2].weight; /*设置这个父结点权*/
}
}/*哈夫曼树建立完毕*/
void Huffman::CreateHuffmanCode()
/*从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码*/
{
char cd[MAX_CHAR_LENGTH];
int i;
unsigned int c;
int start;
int p;
cd[MAX_CHAR_LENGTH-1]='\0'; /*从右向左逐位存放编码,首先存放编码结束符*/
for (i=1;i<=CHAR_NUM;i++) /*求n个叶子结点对应的哈夫曼编码*/
{
hc[i].ch=ht[i].ch;
start=MAX_CHAR_LENGTH-1;
for (c=i,p=ht[i].parent; p!=0; c=p,p=ht[p].parent) /*从叶子到根结点求编码*/
if (ht[p].LChild == c)
cd[--start]='0'; /*左分支标0*/
else
cd[--start]='1'; /*右分支标1*/
/*for循环结点条件为 *p=0表示c是根结点了,而每循环一次又由cd[--start]倒序记录了
每一步是父结点的左或右结点,也就是形成了H编码,并放在cd中*/
strcpy(hc[i].HfmCode,&cd[start]);
}
}
bool Huffman::CreateHuffmanTreeFromFile(char* fn)
{
if (!CreateWeightFromFile(fn))
return false;
CreateHuffmanTree();
CreateHuffmanCode();
return true;
}
void Huffman::ShowCode()
{
for (int i=1;i<=CHAR_NUM;i++)
{
cout<<hc[i].ch<<":\t"<<hc[i].HfmCode<<endl;
}
}
int Huffman::findcode(char ch)
{
int i;
for (i=1;i<=CHAR_NUM;i++)
if (st[i]==ch)
return i;
return 0;
}
bool Huffman::EnCode(char* fs, char* fd)
{
fstream rf;
fstream wf;
rf.open(fs,ios::in|ios::binary);
wf.open(fd,ios::out|ios::binary);
if (!rf||!wf)
return false;
char ch[2];
int num;
while (!rf.eof())
{
rf.read(ch,1);
if (rf.gcount()==1)
{
ch[1] = 0;
num=findcode(ch[0]);
if (num!=0)
wf.write(hc[num].HfmCode, strlen(hc[num].HfmCode));
else
return false;
}
}
rf.close();
wf.close();
return true;
}
bool Huffman::DeCode(char* fs, char* fd)
{
char cha[2];
HTNode p=ht[2*CHAR_NUM-1]; /*p为根结点*/
fstream rf,wf;
rf.open(fs,ios::in|ios::binary);
wf.open(fd,ios::out|ios::binary);
if (!fs||!wf)
return false;
while (!rf.eof()) /*译码段*/
{
rf.read(cha,1);
//if (rf.gcount()==1)
//{要多读入一次对最后一个字符进行解码
if (p.LChild!=0 || p.RChild!=0)/*如果p有子结点*/
{
if (cha[0]=='0' && p.LChild!=0)
p=ht[p.LChild]; /*如果读出的字符为0,p的左结点不为空p指向p的左结点*/
else
if (cha[0]=='1' && p.RChild!=0)
p=ht[p.RChild];/*如果读出的字符为1,p的右结点不为空p指向p的右结点*/
}
else
{
wf.write((char*)&p.ch,1);
p=ht[2*CHAR_NUM-1]; /*重新让p指向根结点*/
if (cha[0]=='0' && p.LChild!=0) /*同上面*/
p=ht[p.LChild];
else
if (cha[0]=='1' && p.RChild!=0)
p=ht[p.RChild];
}
//}
}
rf.close();
return true;
}
bool SaveAs(char* fn, Huffman& h)//把对象存入一个文件
{
fstream f;
f.open(fn,ios::out|ios::binary);
if (!f)
return false;
f.write((char*)&h, sizeof(h));
f.close();
return true;
}
bool LoadFrom(char* fn, Huffman& h)//从文件读入对象数据
{
fstream f;
f.open(fn,ios::in|ios::binary);
if (!f)
return false;
f.read((char*)&h, sizeof(h));
f.close();
return true;
}
#endif
//此处是C/C++代码