赫夫曼树介绍与实现

先了解几个概念:
- 树节点的带权路径长度为从该节点到树根之间的路径长度与节点上权的乘积。
- 树的带权路径长度为树中所有叶子节点的带权路径长度之和。
- 带权路径长度最小的二叉树称做最优二叉树或赫夫曼树。

构造赫夫曼树的通常方法:
1. 根据给定的 n 个权值 { w1 , w2 , …, wn }构成n棵二叉树的集合 F={ T1 , T2 , …, Tn },其中每棵二叉树 Ti 中只有一个带权为 wi 的根节点,其左右节点均为空。
2. 在F中选取两棵根节点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根节点的权值为其左右子树上根节点的权值之和。
3. 在F中删除这两棵树,同时将新得到的二叉树加入F中。
4. 重复(2)和(3),直到F只含一棵树为止。这棵树便是赫夫曼树。

霍夫曼编码:
霍夫曼编码(Huffman Coding)是一种编码方法,霍夫曼编码是可变字长编码(VLC)的一种。霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。

例如使用赫夫曼编码对文本进行编码:
每种字符在电文中出现的次数为 wi , 编码长度为 li ,电文中只有n种字符。那么编码总长为 ni=0wili 。如果将 wi li 分别认为是树的叶子节点的权重和路径长度, ni=0wili 为二叉树上带权路径长度。设计电文总厂最短的二进制前缀编码即为n种字符出现的频率作权,设计一棵赫夫曼树的问题。

下面给出实现代码:

package huffman;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;

/**
* @ClassName: Huffman
* @author xhf
* @date 2017年11月18日
* @Description: 霍夫曼编码问题
*/
public class Huffman {  

    /**
    * @Fields rootNode : 赫夫曼树的根节点
    */
    private Node rootNode;

    /**
    * @Fields nodeList : 存放统计后的节点
    */
    private List<Node> nodeList = new LinkedList<>();

    /**
    * @Title: createHuffmanTree
    * @param @return 
    * @return Node
    * @throws
    * @Description: 根据给定的字符出现的次数和字符信息构造霍夫曼树
    */
    public void createHuffmanTree(String data) {
        /**
         * 统计字符串中字符出现的次数比重
         */
        this.statistics(data);
        List<Node> tempNodeList = new ArrayList<>();
        tempNodeList.addAll(nodeList);
        /**
         * 根据霍夫曼树构造规则构造霍夫曼树
         */
        while (tempNodeList.size() > 1) {
            List<Node> minList = this.getMinList(tempNodeList);
            Node minOne = minList.get(0);
            Node minTwo = minList.get(1);
            tempNodeList.remove(minOne);
            tempNodeList.remove(minTwo);
            Node newNode = new Node();
            minOne.setCode((byte)0);
            minTwo.setCode((byte)1);
            newNode.setLeftChild(minOne);
            minOne.setParent(newNode);
            newNode.setRightChild(minTwo);
            minTwo.setParent(newNode);
            newNode.setWeight(minOne.getWeight() + minTwo.getWeight());
            tempNodeList.add(newNode);
        }
        this.rootNode = tempNodeList.size() == 1 ? tempNodeList.get(0) : null;
    }


    /**
    * @Title: statistics
    * @param @param data 
    * @return void
    * @throws
    * @Description: 统计字符串中各字符出出现的次数
    */
    public void statistics(String data) {
        char[] datachars = data.toCharArray();
        HashMap<Character, Integer> dataMap = new HashMap<>();

        int sum = 0;
        for (char c : datachars) {
            if (dataMap.get(c) == null) {
                dataMap.put(c, 1);
            } else {
                dataMap.put(c, dataMap.get(c)+1);
            }
            sum += 1;
        }

        for (Entry<Character, Integer> e : dataMap.entrySet()) {
            Node node = new Node();
            node.setData(e.getKey());
            node.setWeight((double)e.getValue() / sum);
            nodeList.add(node);
        }

    }


    /**
    * @Title: getMinList
    * @param @param nodeList
    * @param @return 
    * @return List<Node>
    * @throws
    * @Description: 获取当前集合中的最小的两个节点
    */
    public List<Node> getMinList(List<Node> nodeList) {
        List<Node> minList = new ArrayList<>();

        Node minOne = null;
        Node minTwo = null;
        for (Node node : nodeList) {
            if (minOne == null) {
                minOne = node;
                continue;
            }
            if (minTwo == null) {
                if (node.getWeight() >= minOne.getWeight()) {
                    minTwo = node;
                } else {
                    minTwo = minOne;
                    minOne = node;
                }
                continue;
            }
            {
                if (node.getWeight() < minOne.getWeight()) {
                    minTwo = minOne;
                    minOne = node;
                } else if (node.getWeight() < minTwo.getWeight()) {
                    minTwo = node;
                }
                continue;
            }
        }
        minList.add(minOne);
        minList.add(minTwo);
        return minList;
    }

    /**
    * @Title: decode
    * @param @return 
    * @return String
    * @throws
    * @Description: 根据构造的霍夫曼树得到霍夫曼编码
    */
    public String encode(String data) {
        this.createHuffmanTree(data);
        char[] chars = data.toCharArray();

        StringBuffer sb = new StringBuffer();
        for (char c : chars) {
            for (Node n : nodeList) {
                if (n.getData() == c) {
                    sb.append(this.getNodeCode(n));
                }
            }
        }
        return sb.toString();
    }

    /**
    * @Title: decode
    * @param @param code
    * @param @return 
    * @return String
    * @throws
    * @Description: 根据霍夫曼树将赫夫曼编码转为初始文本
    */
    public String decode(String code) {
        if (rootNode == null)
            return "";
        char[] chars = code.toCharArray();
        StringBuffer sb = new StringBuffer();

        Node finder = rootNode;  

        for (char c : chars) {

            if (c == '0') {
                finder = finder.getLeftChild();
            } else {
                finder = finder.getRightChild();
            }
            if (finder.getLeftChild() == null) {
                sb.append(finder.getData());
                finder = rootNode;
            }
        }
        return sb.toString();
    }

    /**
    * @Title: getNodeCode
    * @param @param n
    * @param @return 
    * @return String
    * @throws
    * @Description: 从叶子节点向上遍历得到字符的编码
    */
    public String getNodeCode(Node n) {
        StringBuffer sb = new StringBuffer();
        while (n != null && n.getCode() != null) {
            sb.append(n.getCode());
            n = n.getParent();
        }
        return sb.reverse().toString();
    }

    public static void main(String[] args) {
        Huffman hm = new Huffman();
        String code = hm.encode("System.out.println(decode);");
        String decode = hm.decode(code);
        System.out.println(code);
        System.out.println(decode);
    }

}
/**
* @ClassName: Node
* @author xhf
* @date 2017年11月18日
* @Description: 用于构造二叉树的节点
*/
class Node {

    /**
    * @Fields weight : 权重
    */
    private Double weight;

    /**
    * @Fields code : 该节点的对应 0 1。左孩子0, 右孩子1
    */
    private Byte code;

    /**
    * @Fields data : 节点中存储的字符
    */
    private Character data;

    /**
    * @Fields leftChild : 左孩子
    */
    private Node leftChild;

    /**
    * @Fields rightChild : 右孩子
    */
    private Node rightChild;

    /**
    * @Fields parent : 父节点
    */
    private Node parent;
    public Double getWeight() {
        return weight;
    }
    public void setWeight(Double weight) {
        this.weight = weight;
    }
    public Byte getCode() {
        return code;
    }
    public void setCode(Byte code) {
        this.code = code;
    }
    public Character getData() {
        return data;
    }
    public void setData(Character data) {
        this.data = data;
    }
    public Node getLeftChild() {
        return leftChild;
    }
    public void setLeftChild(Node leftChild) {
        this.leftChild = leftChild;
    }
    public Node getRightChild() {
        return rightChild;
    }
    public void setRightChild(Node rightChild) {
        this.rightChild = rightChild;
    }
    public Node getParent() {
        return parent;
    }
    public void setParent(Node parent) {
        this.parent = parent;
    }

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值