数据结构知识点——哈夫曼树

7、哈夫曼树

7.1、哈夫曼树的概述

  哈夫曼树,也称最优二叉树,它是n个带权叶子结点构成的所有二叉树中,带权路径长度最小的二叉树。

  所谓树的带权路径长度,就是树中所有的叶节点的权值乘上其到根结点的路径长度。

  权值越大的结点离根结点越近的二叉树才是最优二叉树。

  树的带权路径路径长度(WPL)是从树根到每一结点的路径长度之和,记为WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。

(a)WPL = 9*2 + 4*2 + 2*2 + 5*2 = 40

(b)WPL = 9 + 4*2 + 2*3 + 5*3 = 38

(c)WPL = 9 + 5*2 + 4*3 + 2*3 = 37

所以(c)是最优二叉树。

7.2、构建哈夫曼树的流程分析

  给出一组数字,对这组数字进行排序,排序方式如下:

  (1)取出根节点权值最小的两颗二叉树

  (2)组成一颗新的二叉树,前面取出来的两颗二叉树是新二叉树的两个子树

  (3)新二叉树的根节点的权值是前两个二叉树的根节点的权值之和

如图:

7.3、哈夫曼树的代码实现

(1)定义哈夫曼树数据结构:

public class Node<E> {
    E data;
    double weight;
    Node leftChild;
    Node rightChild;

    public Node(E data, double weight) {
        super();
        this.data = data;
        this.weight = weight;
    }

    public String toString() {
        return "Node[data=" + data + ", weight=" + weight + "]";
    }
}

(2)根据树的结点形成哈夫曼树:

    /**
     * 构造哈夫曼树
     *
     * @param nodes
     *            节点集合
     * @return 构造出来的哈夫曼树的根节点
     */
    private static Node createTree(List<Node> nodes) {
        // 只要nodes数组中还有2个以上的节点
        while (nodes.size() > 1) {
            quickSort(nodes);
            //获取权值最小的两个节点
            Node left = nodes.get(nodes.size()-1);
            Node right = nodes.get(nodes.size()-2);

            //生成新节点,新节点的权值为两个子节点的权值之和
            Node parent = new Node(null, left.weight + right.weight);

            //让新节点作为两个权值最小节点的父节点
            parent.leftChild = left;
            parent.rightChild = right;

            //删除权值最小的两个节点
            nodes.remove(nodes.size()-1);
            nodes.remove(nodes.size()-1);

            //将新节点加入到集合中
            nodes.add(parent);
        }

        return nodes.get(0);
    }

    /**
     * 将指定集合中的i和j索引处的元素交换
     *
     * @param nodes
     * @param i
     * @param j
     */
    private static void swap(List<Node> nodes, int i, int j) {
        Node tmp;
        tmp = nodes.get(i);
        nodes.set(i, nodes.get(j));
        nodes.set(j, tmp);
    }

    /**
     * 实现快速排序算法,用于对节点进行排序
     *
     * @param nodes
     * @param start
     * @param end
     */
    private static void subSort(List<Node> nodes, int start, int end) {
        if (start < end) {
            // 以第一个元素作为分界值
            Node base = nodes.get(start);
            // i从左边搜索,搜索大于分界值的元素的索引
            int i = start;
            // j从右边开始搜索,搜索小于分界值的元素的索引
            int j = end + 1;
            while (true) {
                // 找到大于分界值的元素的索引,或者i已经到了end处
                while (i < end && nodes.get(++i).weight >= base.weight)
                    ;
                // 找到小于分界值的元素的索引,或者j已经到了start处
                while (j > start && nodes.get(--j).weight <= base.weight)
                    ;

                if (i < j) {
                    swap(nodes, i, j);
                } else {
                    break;
                }
            }

            swap(nodes, start, j);

            //递归左边子序列
            subSort(nodes, start, j - 1);
            //递归右边子序列
            subSort(nodes, j + 1, end);
        }
    }

    public static void quickSort(List<Node> nodes){
        subSort(nodes, 0, nodes.size()-1);
    }

    //广度优先遍历
    public static List<Node> breadthFirst(Node root){
        Queue<Node> queue = new ArrayDeque<Node>();
        List<Node> list = new ArrayList<Node>();

        if(root!=null){
            //将根元素加入“队列”
            queue.offer(root);
        }

        while(!queue.isEmpty()){
            //将该队列的“队尾”元素加入到list中
            list.add(queue.peek());
            Node p = queue.poll();

            //如果左子节点不为null,将它加入到队列
            if(p.leftChild != null){
                queue.offer(p.leftChild);
            }

            //如果右子节点不为null,将它加入到队列
            if(p.rightChild != null){
                queue.offer(p.rightChild);
            }
        }

        return list;
    }

(3)测试方法:

public static void main(String[] args) {
        List<Node> nodes = new ArrayList<Node>();

        nodes.add(new Node("A", 40.0));
        nodes.add(new Node("B", 8.0));
        nodes.add(new Node("C", 10.0));
        nodes.add(new Node("D", 30.0));
        nodes.add(new Node("E", 10.0));
        nodes.add(new Node("F", 2.0));

        Node root = HuffmanTree.createTree(nodes);

        System.out.println(breadthFirst(root));

    }

结果:

[Node[data=null, weight=100.0], Node[data=A, weight=40.0], Node[data=null, weight=60.0], Node[data=null, weight=30.0], Node[data=D, weight=30.0], Node[data=C, weight=10.0], Node[data=null, weight=20.0], Node[data=null, weight=10.0], Node[data=E, weight=10.0], Node[data=F, weight=2.0], Node[data=B, weight=8.0]]

原文地址:https://www.cnblogs.com/strong-FE/p/12152009.html

时间: 2024-10-12 03:52:12

数据结构知识点——哈夫曼树的相关文章

20172303 2018-2019-1《程序设计与数据结构》哈夫曼树编码与解码

20172303 2018-2019-1<程序设计与数据结构>哈夫曼树编码与解码 哈夫曼树简介 定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree).哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近. 带权路径长度(Weighted Path Length of Tree,简记为WPL) 结点的权:在一些应用中,赋予树中结点的一个有某种意义的实数. 结点的带权路径长度:结点到树根之间的路径长度与

数据结构例程——哈夫曼树

本文是数据结构基础系列(6):树和二叉树中第15课时哈夫曼树的例程. #include <stdio.h> #include <string.h> #define N 50 //叶子结点数 #define M 2*N-1 //树中结点总数 //哈夫曼树的节点结构类型 typedef struct { char data; //结点值 double weight; //权重 int parent; //双亲结点 int lchild; //左孩子结点 int rchild; //右孩

【数据结构】赫夫曼树的实现和模拟压缩(C++)

赫夫曼(Huffman)树,由发明它的人物命名,又称最优树,是一类带权路径最短的二叉树,主要用于数据压缩传输. 赫夫曼树的构造过程相对比较简单,要理解赫夫曼数,要先了解赫夫曼编码. 对一组出现频率不同的字符进行01编码,如果设计等长的编码方法,不会出现混淆的方法,根据规定长度的编码进行翻译,有且只有一个字符与之对应.比如设计两位编码的方法,A,B,C,D字符可以用00-11来表示,接收方只要依次取两位编码进行翻译就可以得出原数据,但如果原数据只由n个A组成的,那发出的编码就是2n个0组成,这样的

数据结构_哈夫曼树

基本概念 路径:在一棵树中,从任意一个结点到达另一个结点的通路 路径长度:该路径所需经过的边的个数 带权路径长度:从根结点到达该节点的路径长度再乘以该结点权值的结果 带权路径长度和:树所有的叶子结点的带权路径长度和 哈夫曼树:给定n个带权值结点,以它们为叶子结点构造的一棵带权路径和最小的二叉树 哈夫曼树的求法 将所有结点放入集合 K. 若集合 K 中剩余结点大于 2 个,则取出其中权值最小的两个结点,构造他们同时为某个新节点的左右儿子,该新节点是他们共同的双亲结点,设定它的权值为其两个儿子结点的

数据结构之哈夫曼树

#include <iostream> #include <iomanip> #include <string> using namespace std; typedef struct { string name; int weight; int parent, lchild, rchild; int visited; //设置visited选项来表示每次查找最小权值后的删除,0代表未删除,1表示删除 }HTNode,*HuffmanTree; int Min(Huff

数据结构之哈夫曼树(java实现)-(五)

所谓哈夫曼树就是要求最小加权路径长度,这是什么意思呢?简而言之,就是要所有的节点对应的路径长度(高度-1)乘以该节点的权值,然后保证这些结果之和最小. 哈夫曼树最常用的应用就是解决编码问题.一般我们用的ASCII是固定长度的编码,对于那些常用的字符,使用很长的长度就显得略为浪费空间了. 下面以一个实例来构建一颗哈夫曼编码树. 设字符集S={A,B,C,D,E,F},字符出现的频率W={2,3,5,7,9,12},对字符集进行哈夫曼编码 (1)以频率为树的节点值,构建6个树节点,保存在一个数据集合

基础数据结构-二叉树-赫夫曼树的解码(详解)

本篇是上一篇赫夫曼树构建与编码的后续,稍微详细讲一下解码的算法. Huffman解码算法流程: 1.定义指针p指向赫夫曼树结点,实际是记录结点数组的下标: 2.定义指针i指向编码串,定义ch逐个取编码串的字符: 3.初始化:读入编码串,设置p指向根结点,i为0: 4.执行以下循环: a)取编码串的第i个字符放入ch: b)如果ch是字符0,表示往左孩子移动,则p跳转到右孩子: c)如果ch是字符1,表示往右孩子移动,则p跳转到右孩子: d)如果ch非0非1,表示编码串有错误,输出error表示解

【数据结构】哈夫曼树实现编码译码

根据一段字符串中字符的个数 作为该字符的权值生成哈夫曼树. 然后根据生成的哈夫曼编码,对任意字符串实现编码,对任意二进制串实现译码. 程序运行结果: 1.程序主界面: 2.根据字符串 创建哈夫曼树及编码: 3.生成的编码表如下: 4.根据生成的哈夫曼编码对字符串编码: 5.生成的编码保存在文件中: 6.对二进制串译码: 结果: 代码: 哈夫曼树的生成和编码的常见,以及编码和译码函数 //_HuffmanTree_H #ifndef _HuffmanTree_H #define _HuffmanT

数据结构 - 赫夫曼树及其应用

赫夫曼树及其应用 赫夫曼(Huffman)树又称最优树,是一类带权路径长度最短的树,有着广泛的应用. 1 基本概念 ① 结点路径:从树中一个结点到另一个结点的之间的分支构成这两个结点之间的路径. ② 路径长度:结点路径上的分支数目称为路径长度. ③ 树的路径长度:从树根到每一个结点的路径长度之和. ④ 结点的带权路径长度:从树的根结点到该结点的的路径长度与结点的权(值)的乘积. 权(值):各种开销.代价.频度等的抽象称呼. ⑤ 树的带权路径长度:树中所有叶子结点的带权路径长度之和,记做: WPL