求赫夫曼编码的算法

求赫夫曼编码的算法

参考清华大学出版社出版的《数据结构(c语言版)》一书,在java下实现

//数据结构
class HuffmanNode{
    public int weight;//权重
    public int parent,lchild,rchild;//父节点、孩子节点在数组中的下标位置

    public HuffmanNode(int weight,int parent,int lchild,int rchild){
        this.weight = weight;
        this.parent = parent;
        this.lchild = lchild;
        this.rchild = rchild;
    }
}

    /**
     * @param str_weight_arr 存放字符权值的数组
     * @param n 字符的数量
     * @return 计算好的赫夫曼编码
     * */
    public String huffmanCoding(int[] str_weight_arr,int n){

        if(n<1 || str_weight_arr.length <n)
            return null;
        //要构造的赫夫曼树的结点数量为叶子结点数量的二倍减1
        int node_num = n*2-1;
        //创建赫夫曼树结点数组,数组大小为node_num+1,我们从下标1开始使用
        HuffmanNode[] huffmanTree = new HuffmanNode[node_num+1];
        //将str_weight_arr中的值放入前n个结点中,这前n个元素就是赫夫曼树的叶子结点
        //而剩下的元素则是属于非叶子结点的位置,也就是我们下面需要动态添加的
        for(int i=1;i<=n;i++)
            huffmanTree[i] = new HuffmanNode(str_weight_arr[i], 0, 0, 0);
        //初始化剩下的元素
        for(int i=n+1;i<=node_num;i++)
            huffmanTree[i] = new HuffmanNode(0, 0, 0, 0);

        //建立赫夫曼树,从下标为n+1的元素开始,逐个建立子树。i的孩子结点是从1到i-1之间权重最小的
        //两个结点,当遍历结束后,下标为node_num的元素就是赫夫曼树的根结点
        for(int i=n+1;i<=node_num;i++){
            //在huffmanTree中下标为1到i-1之间寻找权重最小并且没有父节点的两个元素的下标
            int[] child_arr = selectMinTwo(huffmanTree,1,i-1);
            //判断下child_arr非空
            if(child_arr == null)
                return null;

            //构造新的子树,根结点的下标为i,左右子树的位置为child_arr[0],child_arr[1]
            huffmanTree[i].lchild = child_arr[0];
            huffmanTree[i].rchild = child_arr[1];
            huffmanTree[i].weight = huffmanTree[child_arr[0]].weight
                    +huffmanTree[child_arr[1]].weight;
            huffmanTree[child_arr[0]].parent = i;
            huffmanTree[child_arr[1]].parent = i;
        }//end for

        //此时,从下标为n+1到node_num的位置上,这些非叶子结点的权重在逐渐增大。

        //接下来要生成赫夫曼编码了
        String[] huffmanCode = new String[n+1];
        //code_temp数组中的全部元素就是一个字母的赫夫曼编码
        char[] code_temp = new char[n];
        //将huffmanCode塞满,下标从1开始
        for(int i=1;i<=n;i++){
            int start = n-1;
            //我们要求的是给定的字符串的赫夫曼编码,方法传入的是和字符串中字符一一对应的权重数组
            //所以,要按照顺序,从每个结点开始,向上找,直到找到跟结点为止,用0和1记录期间的路径
            //从叶子到根逆向求编码 f==0时说明已经找到了根结点
            for(int c=1,f=huffmanTree[i].parent;f!=0;c=f,f=huffmanTree[f].parent){
                //c位置的结点不是f位置结点的左子树就是f位置结点的右子树,没得跑
                if(huffmanTree[f].lchild == c)
                    code_temp[--start] = ‘0‘;
                else
                    code_temp[--start] = ‘1‘;
            }
            //第i个字符的赫夫曼码找出来了,将code_temp数组的第start位置到最后的字符截断
            //赋值给huffmanCode[i]
            huffmanCode[i] = String.copyValueOf(code_temp, start, code_temp.length-start);
        }

        String result = "";
        for(String s:huffmanCode)
            result+=s;

        return result;

    }

    private int[] selectMinTwo(HuffmanNode[] huffmanTree,int begin,int end){
        if(begin<1 || end >huffmanTree.length)
            return null;

        int max1 = begin;
        int max2 = begin;
        int max_weight = huffmanTree[begin].weight;
        //寻找最小的权重的元素所在下标
        for(int i=begin;i<=end;i++){
            if(huffmanTree[i].weight<max_weight && huffmanTree[i].parent == 0)
                max1 = i;
        }
        //寻找第二小的权重的元素所在下标
        for(int i=begin;i<=end;i++){
            if(huffmanTree[i].weight<max_weight && huffmanTree[i].parent == 0
                    && max1 != max2)
                max2 = i;
        }

        return new int[]{max1,max2};

    }

}
时间: 2024-08-13 10:33:53

求赫夫曼编码的算法的相关文章

求凸包—— graham_scan算法

求凸包—— graham_scan算法 先按Y-X排序,在按对p0的极角排序,然后进行扫描 Point stk[maxn]; int top; bool cmpYX(const Point A,const Point B)//按Y-X排序 { if(A.y<B.y) return true; if(A.y==B.y){ return A.x<=B.x; } return false; } bool cmp(const Point A,const Point B)//按极角排序 { return

C语言求质数的算法

前言 上次被出了一题质数的C语言求解题目(面试),当时用了最粗暴的算法,回来仔细参考资料,其实答案有很多种: 1,小学生版本: 判断 x 是否为质数,就从 2 一直算到 x-1. static rt_uint32_t array1[ARRAY_LEN]; void func1(void) { for (rt_uint32_t i = 1; i <= ARRAY_LEN; i++) { array1[i - 1] = 0; } rt_uint32_t x, y = 0, z = 0; rt_uin

更快的求整数幂算法

相信整数幂运算作为一个算法演变的例子是再合适不过的了为了节省访客们宝贵的学习时间省去介绍递归等可能涉及到的初级概念的定义.同时如果发现文中有错误的地方请敞开衣服指正. 因为在测试性能时合适的测试数据是必要的,所以本文用C++的大数类进行演示. 点击获取C++大数类源码 这里我们先列一下会提到的算法分析技术: 动态规划 减治法 测试平台: Linux g++ 4.7 原始递归方法 这就不花时间赘述什么了. BigInteger pow(BigInteger x, int N) { if (N ==

链表插入和删除,判断链表是否为空,求链表长度算法的,链表排序算法演示——C语言描述

关于数据结构等的学习,以及学习算法的感想感悟,听了郝斌老师的数据结构课程,其中他也提到了学习数据结构的或者算法的一些个人见解,我觉的很好,对我的帮助也是很大,算法本就是令人头疼的问题,因为自己并没有学习过算法的系统性的课程,现在还是处于不断摸索的阶段,好多算法题目根本就没有什么思路,导致自己对好多题目都很是头疼,就算是自己做过的一些算法的题目,再次遇到也还是不一定会做出来,他给出的建议就是,看懂别人的程序,然后自己去敲,一定会出错,然后调试,有错误接着调试,一直到没有错误为止,并且要时常的去复习

线段求交点的算法

算法一: 求两条线段所在直线的交点, 再判断交点是否在两条线段上. 求直线交点时 我们可通过直线的一般方程 ax+by+c=0 求得(方程中的abc为系数,不是前面提到的端点,另外也可用点斜式方程和斜截式方程,此处暂且不论). 然后根据交点的与线段端点的位置关系来判断交点是否在线段上. 公式如下图: 算法一思路比较清晰易懂, 但是性能并不高. 因为它在不确定交点是否有效(在线段上)之前, 就先去计算了交点, 耗费了较多的时间. 如果最后发现交点无效, 那么之前的计算就白折腾了. 而且整个计算的过

求最小生成树(Prim算法)(1075)

Description 求出给定无向带权图的最小生成树.图的定点为字符型,权值为不超过100的整形.在提示中已经给出了部分代码,你只需要完善Prim算法即可. Input 第一行为图的顶点个数n      第二行为图的边的条数e 接着e行为依附于一条边的两个顶点和边上的权值 Output 最小生成树中的边. Sample Input ABCDEF A B 6 A C 1 A D 5 B C 5 C D 5 B E 3 E C 6 C F 4 F D 2 E F 6 Sample Output (

求一个红包算法

m个硬币,n个红包,红包里的硬币数不可以小于b,也不可以大于t,数目是随机的. mnt均为自然数,且 n*b<=m<=n*t. 求红包算法 function getRedPackets(m,n,b,t){ var ret = new Array(n); /*todo*/ return ret; }

网络流求最大流算法

一.网络流的定义:有向图G=(V,E)中,点集中有一源点S,一汇点T.且S入度为0,T出度为0.对于每条边edge,都有一权值函数c,表示其容量,一权值函数f,表示其实际流量. 满足对于任意一条边都有f(edge)<=c(edge). 二.最大流的定义:在不违背网络流的定义下,S到T的最大流量. 三.増广路的思想. 我们先考虑一个网络流:红色数字表示实际流量,蓝色表示边的容量,黄色表示更优的流量. 这个流从S到T的流量是5,但其显然不是最优的. 这个流比上面那个优,而且事实上,这个流就是当前网络

求最短路径(Bellman-Ford算法与Dijkstra算法)

前言 Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的.这时候,就需要使用其他的算法来求解最短路径,Bellman-Ford算法就是其中最常用的一个. 在网络路由中,RIP协议(距离向量路由算法)一般用Bellman-Ford算法,同时由于简单性所以也适用于分布式系统:但是它的复杂度是O(VE),比Dijkstra算法要慢上许多.而OSPF协议,链路状态分组创建的时候一般用Dijkst