算法-贪心算法-哈夫曼编码

一,概述

基本思想:
以字符的使用频率作为权构建棵哈夫曼树,然后利用哈夫曼树对字符进行编码。
如何构造:
每次从树的集合中取出没有双亲权值最小的两棵树作为左右子树,构造一棵新树,新树根节点的权值为其左右孩子结点权值之和,将新树插入到树的集合中。

二,算法设计

1.确定合适的数据结构:
每个节点的权值,双亲,左孩子,右孩子,以及节点的其他信息。

从叶子节点到根的路径,再求出编码。
树的信息(比如节点个数)

static class Node implements Comparable<Node> {
        public String data;//数据信息
        public double weight;  //权重
        public Node left;//左孩子节点
        public Node right;//右孩子节点

        public Node(String data, double weight){//初始化节点信息,数据和权重
            this.data = data;
            this.weight = weight;
        }
        @Override
        public String toString(){
            return "data:"+this.data+";weight:"+this.weight;
        }

        @Override
        public int compareTo(Node other) {//正序 //排序,按升序排序
            if(other.weight > this.weight){
                return -1;
            }
            if(other.weight < this.weight){
                return 1;
            }
            return 0;
        }
    }

2.初始化:
构造n棵结点为n个字符的单结点树集合
T={t1, t2,… tn},每棵树只有一个带权的根结点,值为该字符的使用频率。
3.如果T中只剩下一棵树,则哈夫曼树构造成功,算法结束。否则,从集合T中取出没有双亲且权值最小的两棵树ti和tj;,将它们合并成一棵新树Zk,新树的左孩子为ti,右孩子为tj,Zk的权值为ti和t j的权值之和。
4.从集合T中删去ti,tj, 加入Zk
5.算法结束。

public static  Node createTree(List<Node> nodes){
        //只要nodes数组中还有两个以上的节点
        int i=1;
        while(nodes.size() > 1){
            Collections.sort(nodes);//1先排序数据
            // 获取权值最小的两个节点
            Node left = nodes.get(0);  //第一个
            Node right = nodes.get(1);//第二个
            //生成新节点,新节点的权值为两个子节点的权值之和
            Node parent = new Node(null, left.weight+right.weight);
            //让新节点作为权值最小的两个节点的父节点
            parent.left=left;//较小节点放到左节点
            parent.data="t"+(i++);//新节点的名字
            parent.right=right;//较大节点放到右节点
            //删除权值最小的两个节点
            nodes.remove(left);
            nodes.remove(right);
            //将新生成的节点添加到集合中
            nodes.add(parent);
        }
        return nodes.get(0);
    }

递归求解WPL

 static int WPL(Node root,int deep,int wpl){
        if(root != null){
            //当遍历到叶节点则直接输出当前权值*深度
            if(root.left==null && root.right==null){
                wpl += root.weight * deep;
                return wpl;
            }else{
                wpl += WPL(root.left,deep+1,wpl)+WPL(root.right,deep+1,wpl);
                return wpl;
            }

        }else
            return 0;
    }

全部代码:

import java.util.*;

public class D {
    static class Node implements Comparable<Node> {
        public String data;//数据信息
        public double weight;  //权重
        public Node left;//左孩子节点
        public Node right;//右孩子节点

        public Node(String data, double weight){//初始化节点信息,数据和权重
            this.data = data;
            this.weight = weight;
        }
        @Override
        public String toString(){
            return "data:"+this.data+";weight:"+this.weight;
        }

        @Override
        public int compareTo(Node other) {//正序 //排序,按升序排序
            if(other.weight > this.weight){
                return -1;
            }
            if(other.weight < this.weight){
                return 1;
            }
            return 0;
        }
    }
    public static  Node createTree(List<Node> nodes){
        //只要nodes数组中还有两个以上的节点
        int i=1;
        while(nodes.size() > 1){
            Collections.sort(nodes);//1先排序数据
            // 获取权值最小的两个节点
            Node left = nodes.get(0);  //第一个
            Node right = nodes.get(1);//第二个
            //生成新节点,新节点的权值为两个子节点的权值之和
            Node parent = new Node(null, left.weight+right.weight);
            //让新节点作为权值最小的两个节点的父节点
            parent.left=left;//较小节点放到左节点
            parent.data="t"+(i++);//新节点的名字
            parent.right=right;//较大节点放到右节点
            //删除权值最小的两个节点
            nodes.remove(left);
            nodes.remove(right);
            //将新生成的节点添加到集合中
            nodes.add(parent);
        }
        return nodes.get(0);
    }
    //递归求解WPL
    static int WPL(Node root,int deep,int wpl){
        if(root != null){
            //当遍历到叶节点则直接输出当前权值*深度
            if(root.left==null && root.right==null){
                wpl += root.weight * deep;
                return wpl;
            }else{
                wpl += WPL(root.left,deep+1,wpl)+WPL(root.right,deep+1,wpl);
                return wpl;
            }

        }else
            return 0;
    }

    //哈夫曼树编码,输出为hashmap,key为字符,value为压缩码,此处用到了递归
    public static HashMap<String, String>
        treeEncode(Node nodeTree,String codeString, HashMap<String, String>hfmmap) {
        if(nodeTree!=null)
        {
            if(nodeTree.left==null&& nodeTree.right==null)
            {
                hfmmap.put(nodeTree.data, codeString);
            }
            treeEncode(nodeTree.left, codeString+"0",hfmmap);
            treeEncode(nodeTree.right, codeString+"1",hfmmap);

        }
        return hfmmap;
    }
    //哈夫曼解码,输入树和压缩码,输出为字符 通过树逆推找到节点
    public static String treeDecode (Node nodeTree,String coding) {
        for (int i = 0; i < coding.length(); i++) {
            if (coding.charAt(i) == '0') {
                if (nodeTree.left != null) {
                    nodeTree = nodeTree.left;
                } else {
                    throw new RuntimeException("编码错误!");
                }
            }
            if (coding.charAt(i) == '1') {
                if (nodeTree.right != null) {
                    nodeTree = nodeTree.right;
                } else {
                    throw new RuntimeException("编码错误!");
                }
            }
        }
        return nodeTree.data;
    }

    //广度优先遍历 层序遍历
    public static  List<Node> breadth(Node root){
        List<Node> list = new ArrayList<Node>();
        LinkedList<Node> queue=new LinkedList();
        //Queue<Node> queue = new ArrayDeque<Node>();
        if(root != null){
            //将跟元素入"队列"
            queue.offer(root);
        }
        while(!queue.isEmpty()){
            //将该队列的"队尾"的元素添加到list中
            list.add(queue.peek());
            Node node = queue.pop();//弹出
            //如果左节点不为null,将它们加入队列
            if(node.left != null){
                queue.offer(node.left);
            }
            //如果有节点不为null,将它们加入队列。
            if(node.right != null){
                queue.offer(node.right);
            }
        }
        return list;
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        List<Node> list = new ArrayList<Node>();
        list.add(new Node("a",5));
        list.add(new Node("b",32));
        list.add(new Node("c",18));
        list.add(new Node("d",7));
        list.add(new Node("e",25));
        list.add(new Node("f",13));
        Node root = D.createTree(list);
        System.out.println(D.breadth(root));
        System.out.println(list);
        System.out.println("WPL:"+WPL(root,0,0));
        System.out.println("各个节点的哈夫曼编码为:");
        HashMap<String, String>hfmmap=new HashMap<String, String>();
        Map<String, String>map=treeEncode(root, "", hfmmap) ;
        //编码结果
        for (String keyString : map.keySet()) {
            System.out.print(keyString+":"+map.get(keyString)+";");
        }
        System.out.println();
        System.out.print("输入1000编码,得到其字符为:");
        //解码
        System.out.print(treeDecode(root, "1000"));

    }

}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值