算法学习之最优二叉树(赫夫曼树)

概念

  • 给定n个权值作为n个叶子节点,构造一颗二叉树,若该数的代全路径长度(wpl)达到最小,称这样的的二叉树为最优二叉树,也成霍夫曼树

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

  • 路径和路径长度:

    • 在一棵树中,从一个结点往下可以达到的孩子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根节点到L层结点的路径长度为L-1

  • 结点的权及带权路径长度:

    • 若将树中结点赋给一个有着某种含义的数值,则这个数值成为该节点的权。结点的带权路径长度为:从根节点到该节点之间的路径长度与该节点的权的乘积

  • 树的带权路径长度:树的带权路径长度规定为所有叶子节点的带权路径长度之和,记为WPL(weighted path length),权值越大的节点离根节点越近。

原理

因为带权路径长度是权值*路径长度,要使带权路径长度最小,节点的权值越大,其路径长度就要越小

  • 给定一个序列,将其从小到大排序,这样我们序列的前两个就是权值最小的两个元素

  • 创建一个新的节点,其权值为权值最小的两个元素的权值之和,然后将这两个最小元素,分别放到新节点的左右子节点上,并将新节点的权值放回序列,代替原来的两个元素

  • 重新排序后重复上述操作,直到序列中只剩一个节点,即构建完毕

  • 这样创建出的赫夫曼树的根结点为所有节点的权值之和,根结点的两个子节点是最大节点与其他节点的权值之和构建的新节点,以此类推,权值越大,越靠近根结点,路径越短

赫夫曼编码

  • 赫夫曼编码是哈夫曼树在电讯通信中的经典应用之一

  • 赫夫曼编码广泛地用于数据文件压缩。其压缩率通常都在20%~90%之间

  • 赫夫曼码是可变字长编码的一种

  • 构建好赫夫曼树后,按照左0右1的规则生成编码,因为生成的赫夫曼树后,每个编码都是叶子节点,所以不会产生有两个编码有同样前缀的情况(即每个编码的父节点都不可能是另一个编码),即赫夫曼编码是前缀编码

代码

class  HuffNode implements Comparable<HuffNode> {
    int value;
    HuffNode left;
    HuffNode right;
    char c;
    String huffCode;

    public HuffNode(int value, char c) {
        this.value = value;
        this.left = null;
        this.right = null;
        this.c = c;
        this.huffCode = "";
    }

    public HuffNode(int value) {
        this.value = value;
        this.left = null;
        this.right = null;
        this.huffCode = "";
    }

    public HuffNode() {
        this.value = 0;
        this.left = null;
        this.right = null;
        this.huffCode = "";
    }

    @Override
    public String toString() {
        if (left != null && right != null) {
            return "HuffNode{ " +
                    " value=" + value +
                    " left=" + left.value +
                    " right=" + right.value +
                    " c=" + c +
                    " huffCode=" + huffCode +
                    " }";
        }else if (left != null && right == null){
            return "HuffNode{ " +
                    " value=" + value +
                    " left=" + left.value +
                    " c=" + c +
                    " huffCode=" + huffCode +
                    " }";
        }else {
            return "HuffNode{ " +
                    " value=" + value +
                    " c=" + c +
                    " huffCode=" + huffCode +
                    " }";
        }
    }

    /**
     * 先序遍历
     */
    public void preOrder() {
        System.out.println(this);
        if (this.left != null) {
            this.left.preOrder();
        }
        if (this.right != null) {
            this.right.preOrder();
        }
    }

    /**
     * 设置哈弗曼编码,路径左0右1
     * 哈弗曼编码是前缀编码的原因为:生成哈夫曼树时每个元素都为叶子节点
     * @param c 根据字符查询
     * @param code 存储路径信息,最后保存到huffCode中
     */
    public void setHuffCode(char c , String code) {

        if (this.c == c) {
            String str = "";
            for (int i = 1; i < code.length(); i++) {
                 str = str + code.charAt(i);
            }
            this.huffCode = str;
            return;
        }
        // 左0
        if (this.left != null) {
            code = code+"0";
            this.left.setHuffCode(c,code);
        }
        // 右1
        if (this.right != null) {
            code = code+"1";
            this.right.setHuffCode(c,code);
        }
    }

    /**
     * 实现Comparable接口
     * 实现方法,此为从小到大排序
     *
     * @param o
     * @return
     */
    @Override
    public int compareTo(HuffNode o) {
        return this.value - o.value;
    }
}
class HuffmaTree {

    private ArrayList<HuffNode> huffNodeArrayList ;
    private HuffNode[] arrHuff;
    private HuffNode root;


    public void setHuffNodeArrayList(ArrayList<HuffNode> huffNodeArrayList) {
        this.huffNodeArrayList = new ArrayList<HuffNode>();
        for (int i = 0 ; i < huffNodeArrayList.size() ; i++) {
            HuffNode node = huffNodeArrayList.get(i);
            this.huffNodeArrayList.add(node);
        }
    }

    public void list() {
        Iterator iterator = huffNodeArrayList.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

    public void setRoot(HuffNode root) {
        this.root = root;
    }

    public void preOrder() {
        if (root == null) {
            System.out.println("空");
        }else {
            root.preOrder();
        }
    }
    /**
     * 根据数组中的值放置arrHuff
     * @param arr
     */
    public void setArrHuff(int[] arr) {
        arrHuff = new HuffNode[arr.length];
        for (int i = 0 ; i < arr.length ; i++) {
            HuffNode node = new HuffNode(arr[i]);
            arrHuff[i] = node;
        }
    }

    public HuffNode[] getArrHuff() {
        return arrHuff;
    }

    public ArrayList<HuffNode> getHuffNodeArrayList() {
        return huffNodeArrayList;
    }

    /**
     * 构造哈夫曼树,所有叶子节点的带权路径之和最小的树(路径长度*权值)
     * @param arr
     * @return 返回最优二叉树的根结点
     */
    public HuffNode createHuffmanTree_2(int[] arr) {
        ArrayList<HuffNode> arrayList = new ArrayList();
        for (int value : arr) {
            arrayList.add(new HuffNode(value));
        }
        while (arrayList.size() > 1) {
            // 1. 将数组从小到大排序
            Collections.sort(arrayList);
            // 2. 选择权值最小的两个节点组成树,其根结点为两子节点权值之和
            HuffNode leftNode = arrayList.get(0);
            HuffNode rightNode = arrayList.get(1);
            HuffNode parent = new HuffNode(leftNode.value + rightNode.value);
            parent.right = rightNode;
            parent.left = leftNode;
            // 3. 将根节点权值放回数组中,并重复上述操作
            arrayList.remove(leftNode);
            arrayList.remove(rightNode);
            arrayList.add(parent);
        }
        return arrayList.get(0);
    }
    /**
     * 构造哈夫曼树,所有叶子节点的带权路径之和最小的树(路径长度*权值)
     * @param arr
     */
    public HuffNode createHuffmanTree_1(int[] arr) {
        // 1. 将数组从小到大排序
        QuickSort quickSort = new QuickSort();
        quickSort.quickSort(arr,0,arr.length-1);
        setArrHuff(arr);
        for (int i = 0 ; i < arr.length-1 ; i++) {
            // 2. 选择权值最小的两个节点组成树,其根结点为两子节点权值之和
            int value = arrHuff[i].value + arrHuff[i+1].value;
            HuffNode node = new HuffNode(value);
            node.left = arrHuff[i];
            node.right = arrHuff[i+1];
            // 3. 将根节点权值放回数组中,并重复上述操作
            arrHuff[i+1] = node;
            for (int j = i+1 ; j < arrHuff.length-1 ; j++) {
                for (int k = i+1 ; k < arrHuff.length-1 ; k++) {
                    if (arrHuff[k].value>arrHuff[k+1].value) {
                        HuffNode temp = arrHuff[k];
                        arrHuff[k] = arrHuff[k+1];
                        arrHuff[k+1] = temp;
                    }
                }
            }
        }
        return arrHuff[arrHuff.length-1];
    }

    /**
     * 构造哈夫曼树,所有叶子节点的带权路径之和最小的树(路径长度*权值)
     * @param arr
     * @return 返回最优二叉树的根结点
     */
    public HuffNode createHuffmanTree_3(int[] arr , char[] letter) {
        ArrayList<HuffNode> arrayList = new ArrayList();
        for (int i = 0 ; i < arr.length ; i++) {
            arrayList.add(new HuffNode(arr[i],letter[i]));
        }
        setHuffNodeArrayList(arrayList);
        while (arrayList.size() > 1) {
            // 1. 将数组从小到大排序
            Collections.sort(arrayList);
            // 2. 选择权值最小的两个节点组成树,其根结点为两子节点权值之和
            HuffNode leftNode = arrayList.get(0);
            HuffNode rightNode = arrayList.get(1);
            HuffNode parent = new HuffNode(leftNode.value + rightNode.value);
            parent.right = rightNode;
            parent.left = leftNode;
            // 3. 将根节点权值放回数组中,并重复上述操作
            arrayList.remove(leftNode);
            arrayList.remove(rightNode);
            arrayList.add(parent);
        }
        return arrayList.get(0);
    }

    /**
     * 设置哈夫曼编码
     * @param c 字符,根据字符查找,并记录路径
     * @param code
     */
    public void setHuffCode(char c , String code) {
        if (this.root==null) {
            System.out.println("空");
        }else {
            root.setHuffCode(c,code);
        }
    }
}
public class HuffmanTreeDemo {

    /**
     * 统计字符串中每个字符出现的次数
     * @param str
     * @return
     */
    public static char[][] countTimes(String str) {
        char[][] letter = new char[2][str.length()];
        ArrayList arrayList = new ArrayList();
        for (int i = 0 ; i < str.length() ; i++) {
            arrayList.add(str.charAt(i));
        }
        int temp = 0 ;
        while (arrayList.size() > 0) {
            int i = 0;
            // 每次根据队列的第一个元素进行查询,若有相同元素计数++,并且将其删除
            letter[0][temp] = (char) arrayList.get(0);
            while (i < arrayList.size()) {
                if (letter[0][temp] == (char)arrayList.get(i)) {
                    letter[1][temp]++;
                    arrayList.remove(arrayList.get(i));
                }else {
                    i++;
                }
            }
            temp++;
        }
        char[][] le = new char[2][temp];
        for (int i = 0 ; i < temp ; i++) {
            le[0][i] = letter[0][i];
            le[1][i] = letter[1][i];
        }
        return le;
    }

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最小生成树和最优二叉树是两个不同的算法,分别用于解决不同的问题。 最小生成树算法用于在一个连通无向图中找到一棵包含所有顶点的树,并且使得树的边的值之和最小。其中,常用的最小生成树算法有Prim算法和Kruskal算法。 Prim算法的步骤如下: 1. 选择一个起始顶点作为树的根节点。 2. 从与树相邻的顶点中选择一个值最小的边,并将该边和相邻的顶点加入到树中。 3. 重复步骤2,直到树包含了所有的顶点。 Kruskal算法的步骤如下: 1. 将图中的所有边按照值从小到大进行排序。 2. 依次选择值最小的边,如果该边的两个顶点不在同一个连通分量中,则将该边加入到最小生成树中。 3. 重复步骤2,直到最小生成树包含了所有的顶点。 最优二叉树,也称为哈夫曼树,是一种特殊的二叉树,用于编码和解码数据。哈夫曼树的构造过程如下: 1. 将给定的n个值构成n棵只有一个节点的树,并根据值由小到大进行排序。 2. 取值最小的两棵树作为左右子树构成一颗新二叉树,新二叉树的值为两棵树的值之和。 3. 将构造的新树放入序列的最左边。 4. 重复步骤2和3,直到所有树合并为一棵树为止。 最终得到的树就是哈夫曼树,也就是最优二叉树。 以下是哈夫曼树生成代码的示例: ```python # 定义节点类 class Node: def __init__(self, value, weight): self.value = value self.weight = weight self.left = None self.right = None # 构造哈夫曼树 def build_huffman_tree(values, weights): nodes = [Node(value, weight) for value, weight in zip(values, weights)] while len(nodes) > 1: nodes = sorted(nodes, key=lambda x: x.weight) left = nodes.pop(0) right = nodes.pop(0) parent = Node(None, left.weight + right.weight) parent.left = left parent.right = right nodes.append(parent) return nodes[0] # 测试代码 values = ['A', 'B', 'C', 'D'] weights = [1, 2, 3, 4] root = build_huffman_tree(values, weights) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值