哈夫曼树和哈夫曼编码

构建哈夫曼树

  1. 只有叶子节点有值
  2. 带权路径长度最短的树,权值较大的结点离根较近。
  3. 叶子节点的权值乘叶子节点到根节点的路径长度之和最小
public class HuffmanTree {

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

  /**
   * 把每个节点当做一个独立的树(只有一个根节点的树)
   * 1. 从小到大排序,取出最小的两个组成新树
   * 2. 将新树加入,删除原先两个
   * 3. 重复上面步骤直到只剩一个节点,就是形成的哈夫曼树的根节点
   * @param arr
   * @return
   */
  public static TreeNode createHuffmanTree(int[] arr) {
    List<TreeNode> nodes = new ArrayList<>();
    for (int value:arr) {
      nodes.add(new TreeNode(value));
    }
    Collections.sort(nodes, Comparator.comparingInt(a -> a.id));

    while (nodes.size()>1) {
      TreeNode leftNode = nodes.get(0);
      TreeNode rightNode = nodes.get(1);

      TreeNode parentNode = new TreeNode(leftNode.id + rightNode.id);
      parentNode.left = leftNode;
      parentNode.right = rightNode;

      nodes.remove(leftNode);
      nodes.remove(rightNode);
      nodes.add(parentNode);
      Collections.sort(nodes, Comparator.comparingInt(a -> a.id));
    }
    return nodes.get(0);

  }
}

哈弗曼编码

  1. 统计字符出现的次数,作为叶子节点的权值
  2. 构建哈夫曼树
  3. 从根节点开始,根据左0右1的规则,获得所有字符(叶子节点)对应的01编码的映射
  4. 构建压缩后的编码,一个字节存储8位即8个01二进制
public class HuffmanCode {

  public static void main(String[] args) {

    String str = "i like like like java do you like a java";
    byte[] contentBytes = str.getBytes();
    byte[] zip = huffmanZip(contentBytes);
    System.out.println(byteTobitString((byte) -1));
  }

  static byte[] huffmanZip(byte[] bytes) {
    // 统计字符出现的次数作为权值,构建node数组
    List<Node> nodes = getNodes(bytes);
    // 构建哈夫曼树
    Node huffmanTree = createHuffmanTree(nodes);
    // 获得字符到二进制编码的映射关系
    getCodes(huffmanTree, "", stringBuilder);
    // 按照一个字节8位计算获得压缩后的编码
    byte[] zip = zip(bytes, huffmanCode);
    return zip;
  }

  static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
    StringBuilder stringBuilder = new StringBuilder();
    for (int i = 0; i < bytes.length; i++) {
      stringBuilder.append(huffmanCodes.get(bytes[i]));
    }

//    System.out.println(stringBuilder.toString());
    int len;
    if (stringBuilder.length() % 8 == 0) {
      len = stringBuilder.length() / 8;
    } else {
      len = stringBuilder.length() / 8 + 1;
    }
    int index = 0;
    byte[] by = new byte[len];
    for (int i = 0; i < stringBuilder.length(); i = i + 8) {
      if (i + 8 > stringBuilder.length()) {
        by[index] = (byte) Integer.parseInt(stringBuilder.substring(i), 2);
      } else {
        by[index] = (byte) Integer.parseInt(stringBuilder.substring(i, i + 8), 2);
      }
      index++;
    }
    return by;
  }

  static Map<Byte, String> huffmanCode = new HashMap<>();
  static StringBuilder stringBuilder = new StringBuilder();

  /**
   * @param node
   * @param code          左0右1
   * @param stringBuilder
   */
  static void getCodes(Node node, String code, StringBuilder stringBuilder) {
    StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
    stringBuilder1.append(code);
    if (node != null) {
      if (node.data == null) {
        getCodes(node.left, "0", stringBuilder1);
        getCodes(node.right, "1", stringBuilder1);
      } else {
        huffmanCode.put(node.data, stringBuilder1.toString());
      }
    }
  }

  public static List<Node> getNodes(byte[] bytes) {
    ArrayList<Node> nodes = new ArrayList<>();
    Map<Byte, Integer> counts = new HashMap<>();
    for (byte b : bytes) {
      Integer count = counts.get(b);
      if (count == null) {
        counts.put(b, 1);
      } else {
        counts.put(b, count + 1);
      }
    }

    for (Entry<Byte, Integer> entry : counts.entrySet()) {
      nodes.add(new Node(entry.getKey(), entry.getValue()));
    }

    return nodes;
  }

  public static Node createHuffmanTree(List<Node> nodes) {
    while (nodes.size() > 1) {
      Collections.sort(nodes, Comparator.comparingInt(Node::getWeight));

      Node leftNode = nodes.get(0);
      Node rightNode = nodes.get(1);
      Node parentNode = new Node(null, leftNode.weight + rightNode.weight);
      parentNode.left = leftNode;
      parentNode.right = rightNode;
      nodes.remove(leftNode);
      nodes.remove(rightNode);
      nodes.add(parentNode);

    }

    return nodes.get(0);
  }

  static String byteTobitString(byte b) {
    int temp = b;
    String s = Integer.toBinaryString(b);
    System.out.println(s);
    return s;
  }
}

class Node {

  Byte data;
  int weight;// 权值,字符串出现次数
  Node left;
  Node right;

  public Node(Byte data, int weight) {
    this.data = data;
    this.weight = weight;
  }

  public Byte getData() {
    return data;
  }

  public int getWeight() {
    return weight;
  }

  public Node getLeft() {
    return left;
  }

  public Node getRight() {
    return right;
  }

  @Override
  public String toString() {
    return "Node{" +
        "data=" + data +
        ", weight=" + weight +
        ", left=" + left +
        ", right=" + right +
        '}';
  }

  public void preOrder() {
    System.out.println(this);
    if (this.left != null) {
      this.left.preOrder();
    }
    if (this.right != null) {
      this.right.preOrder();
    }
  }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容