【Java】构建哈夫曼树和输出哈夫曼编码

问题背景

一个单位有12个部门,每个部门都有一部电话,但是整个单位只有一根外线,当有电话打过来的时候,由转接员转到内线电话,已知各部门使用外线电话的频率为(次/天):5 20 10 12 8 43 5 6 9 15 19 32。

利用哈夫曼树算法思想设计内线电话号码,使得接线员拨号次数尽可能少。要求:

(1)依据使用外线电话的频率构造二叉树;

(2)输出设计出的各部门内线电话号码。

思路描述

  • 构建哈夫曼树:将所有节点按权重值从小到大排序,从中取出最小的两个节点node1、node2,然后创建一个新的节点,新节点的左右子节点分别是node1、node2(左节点node1的权重值一定要比右节点node2小),新节点的权重值是node1和node2权重值之和,然后将新节点代替node1和node2加入节点队列中,重复该过程,直到节点队列中只剩下一个节点。
  • 输入哈夫曼编码:在前面的构建好的二叉树中,左分支上标记0,右分支上标注1,从根节点到叶子节点所走过的路径就是该叶子节点的哈夫曼编码

 代码实现

/**
 * 用二叉链表存储二叉树
 */
public class HTNode implements Comparable<HTNode>{
    int weight;//权重
    HTNode left,right;//左右子节点

    public HTNode(int weight) {
        this.weight = weight;
    }

    @Override
    public int compareTo(HTNode o) {
        return this.weight-o.weight;//从小到大排序
    }
    @Override
    public String toString() {
        return super.toString();
    }
}
public class HuffmanTree {
    //构建哈夫曼树
    public static HTNode CreatHuffmanTree(int[] arr){
        //初始化
        if(arr.length<1)
            return null;
        List<HTNode> nodes=new ArrayList<>();
        for(int weight:arr){
            nodes.add(new HTNode(weight));
        }
        Collections.sort(nodes);//HTNodes实现了Comparable接口,基于weight排序
        while (nodes.size()>1){//nodes中只剩下一个时,该节点就是根节点
            //取权值最小的两个结点
            HTNode htNode1 = nodes.get(0);
            HTNode htNode2 = nodes.get(1);

            HTNode parent=new HTNode(htNode1.weight+htNode2.weight);
            parent.left=htNode1;
            parent.right=htNode2;
            nodes.remove(0);
            nodes.remove(0);
            nodes.add(parent);
            Collections.sort(nodes);
        }

        return nodes.get(0);
    }

    public static void creatHuffmanCode(HTNode root,String append,StringBuilder code){
        StringBuilder stringBuilder = new StringBuilder(code);
        stringBuilder.append(append);
        if(root.left==null&&root.right==null){
            System.out.println("权值为"+root.weight+"的节点的哈夫曼编码为"+stringBuilder);
            return;
        }
        creatHuffmanCode(root.left,"0",stringBuilder);
        creatHuffmanCode(root.right,"1",stringBuilder);
    }
    //写个前序遍历验证一下构建的哈夫曼树是否正确
    public static void preOrder(HTNode node){
        if(node==null)
            return;
        System.out.print(node.weight+" ");
        preOrder(node.left);
        preOrder(node.right);
    }
}

 运行结果

public class Main {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        System.out.println("请输入节点个数:");
        int n=sc.nextInt();
        int[] arr = new int[n];
        System.out.println("请输入"+n+"个节点的权值:");
        for(int i=0;i<n;i++){
            arr[i]=sc.nextInt();
        }
        HTNode htNode = HuffmanTree.CreatHuffmanTree(arr);
        System.out.println("前序遍历该哈夫曼树:");
        HuffmanTree.preOrder(htNode);
        System.out.println();
        HuffmanTree.creatHuffmanCode(htNode,"",new StringBuilder());
    }
}

 

 另外

  1. 为什么哈夫曼编码能够保证是前缀编码?                                                                              要编码的字符都在叶子结点上,根结点到每一个叶子结点的路径一定是不同的
  2.  为什么哈夫曼编码能够保证字符编码总长最短?                                                                   哈夫曼树的带权路径长度是最小的,权重越大出现频率越高的字符越接近根结点
  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值