五种数据压缩算法
- 格式:doc
- 大小:163.42 KB
- 文档页数:60
. . . .
. . . . . 哈弗曼编码
A method for the construction of minimum-re-dundancy codes,
耿1数据结构1:高等教育,2005:182—190
严蔚敏,吴伟民.数据结构(C语言版)[M].:清华大学,1997.
桂,林其伟,东华.信息论与编码技术[M].:清华大学,2007.
大有,唐海鹰,舒,等.数据结构[M].:高等教育,2001
压缩实现
速度要求
为了让它(huffman.cpp)快速运行,同时不使用任何动态库,比如STL或者MFC。它压缩1M数据少于100ms(P3处理器,主频1G)。
压缩过程
压缩代码非常简单,首先用ASCII值初始化511个哈夫曼节点:
CHuffmanNode nodes[511];
for(int nCount = 0; nCount < 256; nCount++)
nodes[nCount].byAscii = nCount;
其次,计算在输入缓冲区数据中,每个ASCII码出现的频率:
for(nCount = 0; nCount < nSrcLen; nCount++)
nodes[pSrc[nCount]].nFrequency++;
然后,根据频率进行排序:
qsort(nodes, 256, sizeof(CHuffmanNode), frequencyCompare);
哈夫曼树,获取每个ASCII码对应的位序列:
int nNodeCount = GetHuffmanTree(nodes);
构造哈夫曼树
构造哈夫曼树非常简单,将所有的节点放到一个队列中,用一个节点替换两个频率最低的节点,新节点的频率就是这两个节点的频率之和。这样,新节点就是两个被替换节点的父 . . . .
. . . . . 节点了。如此循环,直到队列中只剩一个节点(树根)。
// parent node
pNode = &nodes[nParentNode++];
// pop first child
pNode->pLeft = PopNode(pNodes, nBackNode--, false);
// pop second child
pNode->pRight = PopNode(pNodes, nBackNode--, true);
// adjust parent of the two poped nodes
pNode->pLeft->pParent = pNode->pRight->pParent = pNode;
// adjust parent frequency
pNode->nFrequency=pNode->pLeft->nFrequency + pNode->pRight->nFrequency
注意事项
有一个好的诀窍来避免使用任何队列组件。ASCII码只有256个,但实际分配了511个(CHuffmanNode nodes[511]),前255个记录ASCII码,而用后255个记录哈夫曼树中的父节点。并且在构造树的时候只使用一个指针数组(ChuffmanNode *pNodes[256])来指向这些节点。同样使用两个变量来操作队列索引(int nParentNode = nNodeCount;nBackNode =
nNodeCount –1)。
接着,压缩的最后一步是将每个ASCII编码写入输出缓冲区中:
int nDesIndex = 0;
// loop to write codes
for(nCount = 0; nCount < nSrcLen; nCount++)
{
*(DWORD*)(pDesPtr+(nDesIndex>>3)) |=
nodes[pSrc[nCount]].dwCode << (nDesIndex&7);
nDesIndex += nodes[pSrc[nCount]].nCodeLength;
}
(nDesIndex>>3): >>3 以8位为界限右移后到达右边字节的前面
(nDesIndex&7): &7 得到最高位. . . . .
. . . . . 此外,在压缩缓冲区中,必须保存哈夫曼树的节点以及位序列,这样才能在解压缩时重新构造哈夫曼树(只需保存ASCII值和对应的位序列)。
解压缩
解压缩比构造哈夫曼树要简单的多,将输入缓冲区中的每个编码用对应的ASCII码逐个替换就可以了。只要记住,这里的输入缓冲区是一个包含每个ASCII值的编码的位流。因此,为了用ASCII值替换编码,我们必须用位流搜索哈夫曼树,直到发现一个叶节点,然后将它的ASCII值添加到输出缓冲区中:
int nDesIndex = 0;
DWORD nCode;
while(nDesIndex < nDesLen)
{
nCode = (*(DWORD*)(pSrc+(nSrcIndex>>3)))>>(nSrcIndex&7);
pNode = pRoot;
while(pNode->pLeft)
{
pNode = (nCode&1) ? pNode->pRight : pNode->pLeft;
nCode >>= 1;
nSrcIndex++;
}
pDes[nDesIndex++] = pNode->byAscii;
}
程序实现
费诺编码
#include
#include
#include
#include . . . .
. . . . . #define M 100
typedef struct Fano_Node
{
char ch;
float weight;
}FanoNode[M];
typedef struct node
{
int start;
int end;
struct node *next;
}LinkQueueNode;
typedef struct
{
LinkQueueNode *front;
LinkQueueNode *rear;
}LinkQueue;
//建立队列
void EnterQueue(LinkQueue *q,int s,int e)
{
LinkQueueNode *NewNode;
//生成新节点
NewNode=(LinkQueueNode*)malloc(sizeof( LinkQueueNode ));
if(NewNode!=NULL)
{
NewNode->start=s;
NewNode->end=e; . . . .
. . . . . NewNode->next=NULL;
q->rear->next=NewNode;
q->rear=NewNode;
}
else
{
printf("Error!");
exit(-1);
}
}
//按权分组
void Divide(FanoNode f,int s,int *m,int e)
{
int i;
float sum,sum1;
sum=0;
for(i=s;i<=e;i++)
sum+=f[i].weight;//
*m=s;
sum1=0;
for(i=s;i
{
sum1+=f[i].weight;
*m=fabs(sum-2*sum1)>fabs(sum-2*sum1-2*f[i+1].weight)?(i+1):*m;
if(*m==i) break;
}
} . . . .
. . . . . void main()
{
int i,j,n,max,m,h[M];
int sta,end;
float w;
char c,fc[M][M];
FanoNode FN;
LinkQueueNode *p;
LinkQueue *Q;
//初始化队Q
Q=(LinkQueue *)malloc(sizeof(LinkQueue));
Q->front=(LinkQueueNode*)malloc(sizeof(LinkQueueNode));
Q->rear=Q->front;
Q->front->next=NULL;
printf("\t***FanoCoding***\n");
printf("Please input the number of node:");
//输入信息
scanf("%d",&n);
//超过定义M,退出
if(n>=M)
{
printf(">=%d",M);
exit(-1);
}
i=1; //从第二个元素开始录入
while(i<=n)
{ . . . .
. . . . . printf("%d weight and node:",i);
scanf("%f %c",&FN[i].weight,&FN[i].ch);
for(j=1;j
{
if(FN[i].ch==FN[j].ch)//查找重复
{
printf("Same node!!!\n"); break;
}
}
if(i==j)
i++;
}
//排序(降序)
for(i=1;i<=n;i++)
{
max=i+1;
for(j=max;j<=n;j++)
max=FN[max].weight
if(FN[i].weight
{
w=FN[i].weight;
FN[i].weight=FN[max].weight;
FN[max].weight=w;
c=FN[i].ch;
FN[i].ch=FN[max].ch;
FN[max].ch=c;
}