我所知道的数据结构之哈夫曼树

前几篇文章我们学习二叉树、顺序二叉树、线索化二叉树、堆,接下来我们继续学习有关于树的下一个应用结构:哈夫曼树

一、什么是哈夫曼树

基本介绍

1.给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度1(wpl)达到最小,称这样的二叉树为最优叉树。也称为哈夫曼树(HuffmanTree),还有的书翻译为霍夫曼树

wpl:weight pathI ength

2.哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近

问题分析

那么这时就有新的问题出现!需要我们解决了

1.什么是路径呢?路径长度是什么?

2.什么是权和带权路径长度是什么?

3.带权路径长度最短的树,那么树的带权路径长度是什么?

什么是路径和路径长度?

在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径

我所知道的数据结构之哈夫曼树_第1张图片

通路中分支的数目称为路径长度。若规定根结点的层数为 1,则从根结点到第L层结点的路径长度为L-1

我所知道的数据结构之哈夫曼树_第2张图片

什么是结点的权和带权路径长度?

若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的

我所知道的数据结构之哈夫曼树_第3张图片

结点的带权路径长度为:从根结点到该结点之间的路径长度该结点的权的乘积

我所知道的数据结构之哈夫曼树_第4张图片

什么是树的带权路径长度?

树的带权路径长度规定为:
所有叶子结点带权路径长度之和,记为WPL(weighted path length)权值越大的结点离根结点越近的二叉树才是最优二叉树

我所知道的数据结构之哈夫曼树_第5张图片

通过不同的WPL认识哈弗曼树

我所知道的数据结构之哈夫曼树_第6张图片

若该树的带权路径长度1(wpl)达到最小,称这样的二叉树为最优叉树。也称为哈夫曼树(HuffmanTree)

哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近

如上图所示我们发现中间的树其实就是哈夫曼树(HuffmanTree),并且满足权值较大的结点离根较近

权值越大的叶子节点越靠近根节点,权值越小的叶子节点越远离根节点。

二、通过示例认识哈夫曼树

将一个权重数列{13, 7, 8,3,29,6,1},求转成一颗赫夫曼树.

思路图解分析

1.将数列{13, 7, 8, 3, 29, 6, 1},进行从小到大排序

我所知道的数据结构之哈夫曼树_第7张图片

2.新数列的每个数看成最简单根节点的二叉树

我所知道的数据结构之哈夫曼树_第8张图片

3.取出权值最小的两颗二叉树组成新的二叉树,为两最小的树之和

我所知道的数据结构之哈夫曼树_第9张图片

4.将新二叉树以跟节点的权重值再次从小到大排序,不断重复步骤

我所知道的数据结构之哈夫曼树_第10张图片

我们发现每次都需要根据节点权重值进行大小排序,那么我们能不能使得对象节点本身就具有比较的功能呢?

答案是有的:我们的自定义对象Node实现Comparable接口

同时当我们发现排序后,数列的前两个就是最小的权值。
我所知道的数据结构之哈夫曼树_第11张图片

那么根据图所示我们来上节点的代码把

//创建节点类  
//为了让Node 对象持续排序Conllections集合排序  
//让Node 实现Comparable接口  
class Node implements Comparable{  
  
    int value;//结点权值  
    Node left;//指向左节点  
    Node right;//指向右节点  
  
    public Node(int value) {  
            this.value = value;  
     }  

     @Override  
     public String toString() {  
            return "Node[" +"value=" + value +']';  
     }  

     @Override  
     public int compareTo(Node o) {  
         //表示从小到大进行排序  
         //从大到小进行排序 - (this.value - o.value);
         return this.value - o.value;  
     }  
}

核心代码分析

public static void createHuffmanTree(int[] arr ){  
  
    /**  
     * 操作思路
     * 1.遍历arr数组
     * 2.将arr数组里的元素构成Node节点
     * 3.将Node 放入ArrayList中
     * 4.进行从小到大排序
     */ 
     List nodes = new ArrayList();  
     for (int itme:arr) {  
        nodes.add(new Node(itme));  
     }  
  
    //进行排序,ArrayList里的sort排序Node 需要实现Comparable接口  
     Collections.sort(nodes);  
  
     System.out.println(nodes);  
}

我们先来测试看看第一步:将数列构成节点并进行从小到大排序

public static void main(String[] args) {  
  
    int arr[] = {13,7,8,3,29,6,1};  
    createHuffmanTree(arr);  
}

运行结果如下:
[Node[value=1], Node[value=3], Node[value=6], Node[value=7], Node[value=8], Node[value=13], Node[value=29]]

我所知道的数据结构之哈夫曼树_第12张图片

如上所示,第一步看来还是成功了的,那么接下来进行第二步

进行第二步:取出权值最小的两颗二叉树组成新的二叉树,为两最小的树之和

public static void createHuffmanTree(int[] arr ){  
  
    //省略第一步:将数列构成节点并进行从小到大排序代码
   /**  
    *  操作思路 
    *  1.取出两个权值最小的节点二叉树
    *  2.根据两个权值最小的二叉树构建成一个新的二叉树
    *  3.删除原先两个权值最小的节点二叉树
    *  4.将新的二叉树放入队列,并构建新的队列
    *  5.新的队列进行从小到大排序 */  
    
    //取出第一个权值最小的二叉树  
    Node leftNode = nodes.get(0);  
    //取出第二个权值最小的二叉树  
    Node rightNode = nodes.get(1);  

    //根据两个权值最小的二叉树构建成一个新的二叉树 同时构建连接  
    Node parentNode  = new Node(leftNode.value + rightNode.value);  
    parentNode.left = leftNode;  
    parentNode.right = rightNode;  

    //删除原先两个权值最小的节点二叉树  
    nodes.remove(leftNode);  
    nodes.remove(rightNode);  

    //将新的二叉树放入队列,并构建新的队列  
    nodes.add(parentNode);  

    Collections.sort(nodes);  

    System.out.println("第一次处理后:"+nodes);
}

运行结果如下:
[Node[value=1], Node[value=3], Node[value=6], Node[value=7], Node[value=8], Node[value=13], Node[value=29]]
第一次处理后:[Node[value=4], Node[value=6], Node[value=7], Node[value=8], Node[value=13], Node[value=29]]

我所知道的数据结构之哈夫曼树_第13张图片

细节规律小结

1.新的二叉树添加后、就会删除原两个权值最小的二叉树
2.处理完后需要重新排序从小到大
3.如思路分析图所示重复步骤操作后队列里只剩下一个节点

整合核心代码

public static Node createHuffmanTree(int[] arr ){  
  
    /**  
     * 操作思路 
     * 1.遍历arr数组 
     * 2.将arr数组里的元素构成Node节点 
     * 3.将Node 放入ArrayList中 
     * 4.进行从小到大排序 
     */ 
     
     List nodes = new ArrayList();  
     for (int itme:arr) {  
            nodes.add(new Node(itme));  
     }  

     while(nodes.size() >1){  

         //进行排序从小到大  
         Collections.sort(nodes);  

         System.out.println(nodes);  

        /**  
        *  操作思路 
        *  1.取出两个权值最小的节点二叉
        *  2.根据两个权值最小的二叉树构建成一个新的二叉树
        *  3.删除原先两个权值最小的节点二叉树 
        *  4.将新的二叉树放入队列,并构建新的队列 
        *  5.新的队列进行从小到大排序 
        */ 
        
        //取出第一个权值最小的二叉树  
        Node leftNode = nodes.get(0);  
        
        //取出第二个权值最小的二叉树  
        Node rightNode = nodes.get(1);  

        //根据两个权值最小的二叉树构建成一个新的二叉树 同时构建连接  
        Node parentNode  = new Node(leftNode.value + rightNode.value);  
        parentNode.left = leftNode;  
        parentNode.right = rightNode;  

        //删除原先两个权值最小的节点二叉树  
        nodes.remove(leftNode);  
        nodes.remove(rightNode);  

        //将新的二叉树放入队列,并构建新的队列  
        nodes.add(parentNode);  
     }  
     //因为队列只剩下最后一个节点所以直接返回即可  
     return nodes.get(0);  
}

接下来让我们验证一下是否如图所示成功绘制成哈夫曼树

使用前序遍历打印留在队列的root 节点,还记得前序遍历吗?

我所知道的数据结构之哈夫曼树_第14张图片

前序遍历特点:前序是先输出父节点,再遍历左子树和右子树

class Node implements Comparable{ 

    //省略Node 逻辑代码
    //添加前序遍历代码
    public void preOrder(){  
  
      System.out.println(this); 
         
      if(this.left!=null){  
        this.left.preOrder();  
      }  
      if(this.right!=null){  
        this.right.preOrder();  
      }  
   }
    
}
//添加根节点遍历方法
public  static void preOrder(Node root){  
  
    if(root!=null){  
        root.preOrder();  
    }else{  
        System.out.println("该哈弗曼树为空!无法遍历");  
    }  
}

我们的数列{13, 7, 8,3,29,6,1},组装成哈弗曼树后的前序遍历,思考一下是什么呢?

public static void main(String[] args) {  
  
    int arr[] = {13,7,8,3,29,6,1};  
    Node root = createHuffmanTree(arr);  
    preOrder(root);
}

运行结果如下:
[Node[value=1], Node[value=3], Node[value=6], Node[value=7], Node[value=8], Node[value=13], Node[value=29]]
[Node[value=4], Node[value=6], Node[value=7], Node[value=8], Node[value=13], Node[value=29]]
[Node[value=7], Node[value=8], Node[value=10], Node[value=13], Node[value=29]]
[Node[value=10], Node[value=13], Node[value=15], Node[value=29]]
[Node[value=15], Node[value=23], Node[value=29]]
[Node[value=29], Node[value=38]]
Node[value=67]
Node[value=29]
Node[value=38]
Node[value=15]
Node[value=7]
Node[value=8]
Node[value=23]
Node[value=10]
Node[value=4]
Node[value=1]
Node[value=3]
Node[value=6]
Node[value=13]

我所知道的数据结构之哈夫曼树_第15张图片

我所知道的数据结构之哈夫曼树_第16张图片

你可能感兴趣的:(java,算法,程序员)