哈夫曼树

百度百科定义

给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

image.png

哈夫曼树数据结构

  • 左子树
  • 右子树
  • 父节点
  • 权重
  • 数据
public static class TreeNode<T> implements Comparable<TreeNode<T>>{
        T data;
        int weight;
        TreeNode leftChild;
        TreeNode rightChild;
        TreeNode parent;
        
        public TreeNode(T data, int wei) {
            this.data = data;
            this.weight = wei;
            leftChild = null;
            rightChild = null;
            parent = null;
        }

        @Override
        public int compareTo(TreeNode<T> o) {
            if (this.weight > o.weight) {
                return -1;
            } else if (this.weight < o.weight) {
                return 1;
            }
            return 0;
        }
    }

哈夫曼树构建

假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:

  • (1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
  • (2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
  • (3 )从森林中删除选取的两棵树,并将新树加入森林;
  • (4) 重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。

举个例子
有这样权值数组 {13,6,8,19,2,36,5,25 }

  • 1 排序
    {2,5,6,8,13,19,25,36}
  • 2 在这些数中 选择两个最小的权值,作为一棵新树node的左右节点(小的在做,大的在右),node的权值为其左、右子树根结点权值之和


    步骤1.png
  • 3 把新节点node的权值加入数组,重新排序
    {7,6,8,13,19,25,36}
    重复步骤2
步骤2.png
  • 4 重复步骤3
    排序后的权值数组 {8,13,13,19,25,36}


    image.png

    排序后的权值数组{13,19,21,25,36}


    image.png

    排序后的权值数组{21,25,32,36}
    image.png

    排序后的权值数组 {32,36,46}
    image.png

    排序后的权值数组{46,68}


    image.png

    排序后的权值数组{114}

public TreeNode createHaffManTree(ArrayList<TreeNode> list) {
        while (list.size() > 1) {
            Collections.sort(list);
            TreeNode left = list.get(list.size()-1);
            TreeNode right = list.get(list.size()-2);
            TreeNode parent = new TreeNode("p", left.weight + right.weight);
            parent.leftChild = left;
            left.parent = parent;
            parent.rightChild = right;
            right.parent = parent;
            list.remove(left);
            list.remove(right);
            list.add(parent);
        }
        root = list.get(0);
        return list.get(0);
    }

哈夫曼树遍历

层次遍历 :使用队列实现层次遍历


我们遍历这棵树


image.png

步骤

  • 1 把根节点加入到队列]
    list.offer(root);

队列{114}

  • 2 队列list不为空,弹出队头,输出
TreeNode node = list.pop();
System.out.println(node.data);
  • 3 读取队头node 的左孩子,和右孩子,如果孩子不为空,孩子加入队列
if (node.leftChild != null) {
    list.offer(node.leftChild);
    }
if (node.rightChild != null) {
    list.offer(node.rightChild);
    }
  • 现在队列的数据{46,68}
  • 弹出46 ,队尾加入46的左孩子,右孩子,队列现在的数据{68,21,25}
  • 弹出68 ,队尾加入68的左孩子,右孩子,队列现在的数据{21,25,32,36}
  • 弹出21 ,队尾加入21的左孩子,右孩子,队列现在的数据{25,32,36,8,13}
  • 弹出25 ,队尾加入25的左孩子,右孩子,25没有左右孩子,队列现在的数据{32,36,8,13}
  • 弹出32 ,队尾加入32的左孩子,右孩子,队列现在的数据{36,8,13,13,19}
  • 弹出36 ,队尾加入32的左孩子,右孩子,36没有左右孩子,队列现在的数据{8,13,13,19}
  • 弹出8 ,队尾加入8的左孩子,右孩子, 没有左右孩子,队列现在的数据{13,13,19}
  • 弹出13 ,队尾加入13的左孩子,右孩子,队列现在的数据{13,19,6,7}
  • 弹出13 ,队尾加入13的左孩子,右孩子,队列现在的数据{19,6,7}
  • 弹出19 ,队尾加入19的左孩子,右孩子,队列现在的数据{6,7}
  • 弹出6,队尾加入6的左孩子,右孩子,队列现在的数据{7}
  • 弹出7,队尾加入7的左孩子,右孩子,队列现在的数据{2,5}
  • 弹出2,队尾加入2的左孩子,右孩子,队列现在的数据{5}
  • 弹出5,队尾加入5的左孩子,右孩子,队列现在的数据{}
  • 队列为空,遍历结束
image.png
public void showHaffman(TreeNode root) {
        LinkedList<TreeNode> list = new LinkedList<>();
        ArrayList<TreeNode> arrayList = new ArrayList<>();
        list.offer(root);
        while (!list.isEmpty()) {
            TreeNode node = list.pop();
            System.out.println(node.data);
            if (node.leftChild != null) {
                list.offer(node.leftChild);
            }
            if (node.rightChild != null) {
                list.offer(node.rightChild);
            }
        }
    }

获取哈夫曼编码

image.png
  • 8 的哈夫曼编码:000
  • 6 的哈夫曼编码:0010
  • 2 的哈夫曼编码:00110
  • 5 的哈夫曼编码:00111
  • 25 的哈夫曼编码:01
  • 13 的哈夫曼编码:100
  • 19 的哈夫曼编码:101
  • 19 的哈夫曼编码:11
image.png
public void getCode(TreeNode node) {
        Stack<String> stack = new Stack<>();
        TreeNode tNode = node;
        while (tNode != null && tNode.parent != null) {
            // left 0, right 1
            if (tNode.parent.leftChild == tNode) {
                stack.push("0");
            } else if (tNode.parent.rightChild == tNode) {
                stack.push("1");
            }
            tNode = tNode.parent;
        }
        while (!stack.isEmpty()) {
            System.out.print(stack.pop());
        }
    }

测试

public static void main(String[] args) {
        ArrayList<TreeNode> list = new ArrayList<>();
        TreeNode<String> node = new TreeNode("good", 54);
        list.add(node);
        list.add(new TreeNode("morning", 10));
        list.add(new TreeNode("afternoon", 20));
        list.add(new TreeNode("hello", 100));
        list.add(new TreeNode("hi", 200));
        HaffmanTree tree = new HaffmanTree();
        tree.createHaffManTree(list);
        tree.showHaffman(tree.root);
        tree.getCode(node);
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容