用C++实现文件压缩(1 哈弗曼编码)
今天下午想把文件压缩写一下,因为我觉得这个还是比较锻炼技术的,对数据结构的要求应该比较高,权当练习了吧。
我采用的压缩方式是Huffman编码,不过比较囧的是,我拼写拼错了,我拼的是haffman,在后面的代码也是出尽洋相。
huffman是非常经典的一种编码形式,不过现在好像用的不多了,但是这种压缩编码的好处在于数据是无损压缩的,而且非常经典。
在构造huffman树,要做的步骤如下:
1 对文件的元素进行统计,其实就是个计数,我这里的元素是以字节为单位的,毕竟任何文件都可以转化为字节流的形式,处理起来方便的很。
2 然后对统计结果进行一次由小到大的排序,排序的key值就是元素出现的次数。
3 接着取出来排好序的数组的前两个元素,求和,并且再其左右子树上创建这两个元素的副本,然后再扔回首部,此时扔回数组中的,就是包含子树的节点了。在这里有个代码优化的小技巧,能在一定程度上提高效率,减少不必要的操作。
4 此时原来已经排好序的数组可能会遭到破坏,必须重新进行排序,因为数组大体是有序的,所以使用此时效率最高的一步插入排序,时间复杂度仅为O(n),我专门写了个函数来实现这个功能,并且进行了简单的测试。
5 然后再取出首部的前两个元素,重复上面步骤3,直到数组中的元素个数为1.
最终数组中仅存的元素,就是一棵二叉树的Root。
这样huffman树的构造就完成了。
在huffman树构造完成后,接着要做的就是根据huffman树获取其huffman编码,这个还是很简单的,就是使用递归,左树传入0,右树传入1,依次递归下去,遇到叶子节点就直接输出,当然了,如果需要添加到某个数据结构中,只要在递归函数中给个参数接口,或者设置一个全局的容器,都能解决,换言之,类似于输出重定向。
这样上面的步骤完成后,就能很好的获取到huffman编码了。不过根据huffman编码结果将字符型的0,1写入到二进制中的0,1还有一些比较关键的函数需要处理,还没想到比较好的点子,不过最基础的蛮力方法肯定是能写的,但是,效率就不好说了。
另外获取huffman编码中,用于0,1传递的参数肯定是要用一个buffer的,这个buffer我用的是char字符串,现在有个问题就是,这个字符串的长度只要大于多少就能满足任何2GB以下文件的huffman编码呢?
我觉得这个题可以用来出一道笔试面试题,好好想一想就能想明白的。我的结果是 254,你觉得呢?
最近笔试面试写了不少题,都是被人考,现在我来考考别人~
另外huffman的解码我也基本想到思路了,明天有时间的话就试着明天实现。
程序运行结果:
第一列是字符的数值,第二列是出现的次数,第三列是其huffman编码。
我这代码放眼望去,尽是被拼成了haffman的错误拼写……
程序代码:
#include <iostream>
#include <windows.h>
#include <vector>
#include <algorithm>
using namespace std; struct HaffmanStruct
{
//a small structure
HaffmanStruct():val(),ncounts(),lNext(NULL),rNext(NULL){}
bool operator < (HaffmanStruct &);
bool operator > (HaffmanStruct &);
void Reset();
unsigned char val;
unsigned int ncounts;
//used for tree
HaffmanStruct * lNext;
HaffmanStruct * rNext;
}; bool HaffmanStruct::operator > (HaffmanStruct & hs)
{
return ncounts > hs.ncounts;
} bool HaffmanStruct::operator < (HaffmanStruct & hs)
{
return hs.ncounts > ncounts;
} void HaffmanStruct::Reset()
{
val = ;
ncounts = ;
lNext = NULL;
rNext = NULL;
} /*
Created in 2013 10 03
ergodic the Haffman tree
*/
template <class T>
void ErgodicTree(T & Root, char * szStr, int nDeep)
{
if(Root.lNext == NULL && Root.rNext == NULL)
cout << (int)Root.val << ends << Root.ncounts \
<< ends <<szStr << endl;
szStr[nDeep] = '';
if(Root.lNext != NULL)
ErgodicTree(*Root.lNext, szStr, nDeep + );
szStr[nDeep] = '';
if(Root.rNext != NULL)
ErgodicTree(*Root.rNext, szStr, nDeep + );
szStr[nDeep] = ;
} /*
Created in 2013 10 03
Used in a Array when only one of its member is not sorted
for ascend sequece
*/
template <class T>
void SingleSort(T * pHead, int nLength, const int nModifiedAddr)
{
if(pHead == NULL || nLength <= )
return;
int nOffSet = ;
int nFront, nBack;
T tmpVal;
for(int i = nModifiedAddr + ;i < nLength;++ i)
{
if(pHead[nModifiedAddr] > pHead[i])
{
nOffSet ++;
}
else
break;
}
if(nOffSet != )
{
tmpVal = pHead[nModifiedAddr];
nFront = nModifiedAddr;
nBack = nModifiedAddr + nOffSet;
while(nFront != nBack)
{
pHead[nFront] = pHead[nFront + ];
nFront ++;
}
pHead[nBack] = tmpVal;
return;
} for(int i = nModifiedAddr - ;i >= ;-- i)
{
if(pHead[nModifiedAddr] < pHead[i])
{
nOffSet --;
}
else
break;
}
if(nOffSet != )
{
tmpVal = pHead[nModifiedAddr];
nFront = nModifiedAddr + nOffSet;
nBack = nModifiedAddr;
while(nFront != nBack)
{
pHead[nBack] = pHead[nBack - ];
nBack --;
}
pHead[nFront] = tmpVal;
return;
} } int _tmain(int argc, _TCHAR* argv[])
{
unsigned int aCharCountArray[] = {};
unsigned char szBuf[];
DWORD dwReadNum;
vector<HaffmanStruct> vecValidNumberArray;
HANDLE hFile = CreateFile(
_T("C:\\record.txt"),
GENERIC_READ,
FILE_SHARE_READ,
,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);
if(hFile == INVALID_HANDLE_VALUE)
return ;
while(ReadFile(hFile, szBuf, , &dwReadNum, NULL))
{
if(dwReadNum == )
break;
for(unsigned int i = ;i < dwReadNum;++ i)
{
//words count
aCharCountArray[szBuf[i]] ++;
}
}
//used in the follow two part's code,temp value
HaffmanStruct ValidStruct;
for(int i = ;i < ;++ i)
{
if(aCharCountArray[i] != )
{
ValidStruct.val = i;
ValidStruct.ncounts = aCharCountArray[i];
vecValidNumberArray.push_back(ValidStruct);
}
}
sort(vecValidNumberArray.begin(), vecValidNumberArray.end());
while(vecValidNumberArray.size() != )
{
ValidStruct.Reset();
ValidStruct.ncounts = vecValidNumberArray[].ncounts + vecValidNumberArray[].ncounts;
ValidStruct.lNext = new HaffmanStruct;
*ValidStruct.lNext = vecValidNumberArray[];
ValidStruct.rNext = new HaffmanStruct;
*ValidStruct.rNext = vecValidNumberArray[];
vecValidNumberArray.erase(vecValidNumberArray.begin());
vecValidNumberArray[] = ValidStruct;
SingleSort(&vecValidNumberArray[], vecValidNumberArray.size(), );
} char Buf[] = {};
ErgodicTree(vecValidNumberArray[], Buf, ); /*
int vecSize = vecValidNumberArray.size();
for(int i = 0;i < vecSize;++ i)
{
cout << (int)vecValidNumberArray[i].val << ends << \
vecValidNumberArray[i].ncounts << endl;
}*/
CloseHandle(hFile);
system("pause");
return ;
}
用C++实现文件压缩(1 哈弗曼编码)的更多相关文章
- C++哈弗曼编码
// haffman.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> using name ...
- js神秘的电报密码---哈弗曼编码
哈夫曼编码,根据每个单词在文本中出现的次数频率为权值,频率高的权值大.然后每次取两个频率最小的生成树,最后生成一颗大树.从根节点到该单词的路径,左边为0,右边为1, function HFM(){ v ...
- 基于哈夫曼编码的文件压缩(c++版)
本博客由Rcchio原创 我了解到很多压缩文件的程序是基于哈夫曼编码来实现的,所以产生了自己用哈夫曼编码写一个压缩软件的想法,经过查阅资料和自己的思考,我用c++语言写出了该程序,并通过这篇文章来记录 ...
- 哈弗曼实现(C++)
HuffmanCode.h #ifndef HUFFMANCODE_H #define HUFFMANCODE_H enum LRSTATUS { LEFTCHILD, //左子树 RIGHTCHIL ...
- java实现哈弗曼树
O(∩_∩)O~~ 概述 我想学过数据结构的小伙伴一定都认识哈弗曼,这位大神发明了大名鼎鼎的“最优二叉树”,为了纪念他呢,我们称之为“哈弗曼树”.哈弗曼树可以用于哈弗曼编码,编码的话学问可就大了,比如 ...
- 哈弗曼树的理解和实现(Java)
哈弗曼树概述 哈弗曼树又称最优树,是一种带权路径长度最短的树,在实际中有广泛的用途.哈弗曼树的定义,涉及路径.路径长度.权等概念.哈弗曼树可以用于哈弗曼编码,用于压缩,用于密码学等. 哈弗曼树的一些定 ...
- K:哈弗曼树
相关介绍: 树形结构除了应用于查找和排序等操作时能调高效率,它在信息通讯领域也有着广泛的应用.哈弗曼(Huffman)树就是一种在编码技术方面得到广泛应用的二叉树,它同时也是一种最优二叉树. 哈弗曼 ...
- 数据压缩之经典——哈夫曼编码(Huffman)
(笔记图片截图自课程Image and video processing: From Mars to Hollywood with a stop at the hospital的教学视频,使用时请注意 ...
- java 哈夫曼编码
//哈夫曼树类 public class HaffmanTree { //最大权值 ; int nodeNum ; //叶子结点个数 public HaffmanTree(int n) { this. ...
随机推荐
- python的加密算法(1):反转加密
说白了,就是把字符串倒序. 在js里,有一个reverse.但是python中没有. 不过,有一个更简单的方法,就是: ‘abcd’ [::-1] 这里,具体解释一下: (参看:https://doc ...
- hdu 5186(模拟)
zhx's submissions Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others ...
- redis的持久化(RDB&AOF的区别)
RDB 是什么? 在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里. Redis会单独创建(fork)一个子进程来进行持久化,会 ...
- HDU 5695 Gym Class && 百度之星 初赛 1006
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5695 本文链接:http://www.cnblogs.com/Ash-ly/p/5515234.htm ...
- Geodetic集合 c++
感谢某位不知名dalao的博客,我才知道怎么解题.... 最开始连题意都读错了....这个故事告诉我们要好好读题 描述 Description 图G是一个无向连通图,没有自环,并且两点之间至多只有一条 ...
- 学习LSM(Linux security module)之一:解读yama
最近打算写一个基于LSM的安全模块,发现国内现有的资料极少.因此打算自己琢磨一下.大致的学习路线如下: 由易至难使用并阅读两到三个安全模块->参照阅读模块自己实现一个安全模块->在自己实现 ...
- WebApi 的三种寄宿方式 (二) - 宿主和控制器不在一个程序集
新建一个类库: SelfHost: 方法一: 1.添加对MyControllers类库的引用. 2.在控制台代码中加入一行代码: 当然,可以添加多个程序集.(记得引用) var config = ne ...
- [Codeforces 35E] Parade
Link: Codeforces 35E 传送门 Brief Intro: 给定$n$个矩形,求出轮廓线的所有顶点 Solution: 对于此类可拆分成多个事件点的题目,使用扫描线的方式 将每个矩形分 ...
- MySQL json 类型操作快速入门
MySQL 5.7.8开始支持 json类型. create table t(id int,js json,PRIMARY KEY (`id`)) 插入数据insert into t values(1 ...
- 9.1(java学习笔记)正则表达式
一.正则表达式 1.1正则表达式 正则表达式是描述一种规则,通过这个规则可以匹配到一类字符串. 2.1正则语法 2.1.1普通字符:字母.数字.下划线.汉字以及没有特殊意义的符号都是普通字符. 正则表 ...