霍夫曼编码c++实现

来源:互联网 发布:淘宝代运营公司的套路 编辑:程序博客网 时间:2024/06/06 02:19

一、霍夫曼编码的原理:
     在数据通信时,可以用0,1码的不同排列来表示字符。例如给定一段报文CAST CAST SAT AT A TASA
,在报文中出现的字符集合是{C,A,S,T},各个字符出现的频度是{2,7,4,5}。若给每个字符一个等
长的二进制表示,例如 C:00 A:01 S:10 T:11,则所发的报文将是00011011 00011011 100111 0111 01 11011001,
共计(2+7+5+4)*2=36个码。
若按字符出现的频度不同给予不同长度的编码,出现频度较大的字符采用
为数较少的编码,出现频度较小的字符采用位书较多的编码,可以是报文的码数降到最小,这就是所谓的最小
冗余编码问题。
霍夫曼编码就能实现这种最小冗余编码。上例中按字符出现的频度进行编码,A:0 T:10 S:110
C:111,则最终的报文只有35个码,节省了传输中使用的单元。


二、核心算法:
    一般情况下,霍夫曼编码的工作主要分为两步。第一步是准备工作,对于需要编码的字符(一般存在于文件里)
进行扫描,统计每个字符出现的频次,得到一个整数数组。第二步根据这个频次数组构造一棵霍夫曼树,这一步
是霍夫曼编码的核心内容。第三步,再次扫描一遍待编码的字符,对每个字符,在霍夫曼树里搜索该字符,得到它的编码。
    用到的主要数据结构包括:最小堆、二叉树和栈。

算法1:构造霍夫曼树 //////////////////////
输入:频次数组weights[]
输出:频次的霍夫曼树(是一种二叉树)
过程:
      1 扫描频次数组weights
        针对每个weights[i],构造一个单节点二叉树,插入最小堆中;
      2 设weights长度为n,循环n-1次
        每次循环都选择二叉树里根的值较小的两棵进行合并,新生成的树
        的根的值=旧的两棵树根的值的和,旧的被合并的两棵二叉树被从
        最小堆里删除,新合并生成的二叉树插入到最小堆里;
      3 最后最小堆里只剩下一棵二叉树,它就是要求的霍夫曼树

算法2:编码算法 //////////////////////////
//从树根开始在HFTree里查找w,向左走记为0
//向右走记为0,最终的01串就是w的编码。
//算法思想类似于二叉树的非递归遍历算法,
//利用栈来存放上一个树节点的信息,
输入:霍夫曼树HFTree,待编码的权值w
输出:编码字符串 str
过程:
     1 声明遍历树节点的变量currnode并初始化为HFTree的根节点,
声明遍历时要用的栈stck
     2 while( currnode不为空 or stck不为空)
        {
            访问currnode->data后,将currnode入栈,currnode指向它的左子树,
            一直往左走,直到左子树为空;
           currnode指向空时,退栈,并让currnode指向当前节点的右孩子;    
        }
    
三、算法实现:


算法1:构造霍夫曼树 //////////////////////
//根据权重生成霍夫曼树
//weight 权重数组
//n  权重数组长度
//NewTree 新生成的霍夫曼树
void createHFMTree(int* weights,int n,BinaryTree<int>& NewTree)
{

    //初始化堆
    MinHeap<BinaryTree<int> > heap(n);
    int i=0;
    for(;i<n;i++){
        heap.Insert(BinaryTree<int>(weights[i]));
    }

    //选择堆中根数值较小的两棵树,组成一棵新树然后插入堆里,并把原来的两棵树从堆里删除
    BinaryTree<int> tree1;
    BinaryTree<int> tree2;
    i=0;    
    for(;i<n-1;i++){
        if(heap.RemoveMin(tree1)==0){
            cout<<"error in createHFMTree--tree1/n";
            exit(0);
        }
        if(heap.RemoveMin(tree2)==0){
            cout<<"error in createHFMTree--tree2/n";
            exit(0);
        }
        heap.Insert(BinaryTree<int>(tree1,tree2));
    }
    if(heap.RemoveMin(NewTree)==0){                    //返回最终的霍夫曼树
        cout<<"error in createHFMTree--NewTree/n";

        exit(0);
    }
    
}

算法2:编码算法 //////////////////////////
//编码
//value 代编码的权值
//tree 霍夫曼树
//code 编码结果(01串)
bool Coding(int value,const BinaryTree<int>& tree,char* code)
{
    BinTreeNode<int>* currnode=tree.GetRoot();  /遍历整个树,指向每一个节点
    stack<BinTreeNode<int>* > stck;                     //遍历树时,存放树节点指针的栈
    int index_code=0;                                                //结果数组的栈顶
    
    while(currnode!=NULL || !stck.empty()){

        //访问currnode->data后,将currnode入栈,遍历左子树
        while(currnode!=NULL){
            //cout<<code<<endl;
            //若数值与value相同,则返回true
            if(value==currnode->GetData())
                return true;

            stck.push(currnode);                                 //currnode入栈
            currnode=currnode->GetLeft();                 //currnode指向它的右孩子

            if(currnode!=NULL)
                code[index_code++]='0';                        //入栈,栈顶加1
        }

        //currnode指向空时,退栈,并让currnode指向当前节点的右孩子
        if(!stck.empty()){
            currnode=stck.top();            
            stck.pop();
            currnode=currnode->GetRight();

            if(currnode!=NULL){
                index_code--;                                        //出栈
                code[index_code++]='1';                      //入栈,栈顶加1    
            }
        }    
    }
    
    return false;
    
}

四、问题与思考:

参考文献:《数据结构》清华大学出版社 殷人昆等

原创粉丝点击