Huffman Tree

Weighted Path Length of a Tree

Suppose a binary tree has nn weighted leaf nodes. The sum of the products of the path lengths from the root to each leaf and the corresponding leaf weight is called the weighted path length of the tree (Weighted Path Length of Tree, WPL).

Let wiw_i be the weight of the ii-th leaf node of the binary tree, and let lil_i be the path length from the root to the ii-th leaf node. The WPL is calculated as follows:

WPL=∑i=1nwiliWPL=\sum_{i=1}^nw_il_i
Figure: huffman tree 1

As shown above, the WPL calculation and result are as follows:

WPL=2∗2+3∗2+4∗2+7∗2=4+6+8+14=32WPL=2*2+3*2+4*2+7*2=4+6+8+14=32

Structure

For a given set of leaf nodes with fixed weights, different binary trees can be constructed. Among them, the binary tree with the smallest WPL is called a Huffman tree (Huffman Tree).

In a Huffman tree, the smaller a leaf node's weight, the farther it is from the root; the larger a leaf node's weight, the closer it is to the root. In addition, only leaf nodes have degree 00, while all other nodes have degree 22.

Huffman Algorithm

The Huffman algorithm is used to construct a Huffman tree. Its steps are as follows:

  1. Initialization: Construct nn binary trees, each consisting of only one root node, from the given nn weights, obtaining a set of binary trees FF.
  2. Selection and merging: Select the two binary trees with the smallest root-node weights from the set of binary trees FF and use them as the left and right subtrees to construct a new binary tree. The weight of the root node of this new binary tree is the sum of the weights of the roots of its left and right subtrees.
  3. Deletion and insertion: Delete the two binary trees used as the left and right subtrees from FF, and add the newly constructed binary tree to FF.
  4. Repeat steps 2 and 3. When only one binary tree remains in the set, that tree is the Huffman tree.
Figure: huffman tree 2

Proof of Correctness

Lemma

In an optimal prefix-code tree (Huffman tree), the two leaf nodes with the smallest weights are always the deepest leaf nodes, and adjusting these two nodes to be siblings does not destroy the optimality of the coding tree.

Proof

We use proof by contradiction to prove this statement. Suppose that in an optimal prefix-code tree, there are two leaf nodes with the smallest weights that are not the deepest leaf nodes. Let these two nodes be aa and bb, and suppose their depths are less than that of some deepest leaf node. For this deepest leaf node cc, we can swap the position of aa with cc, or the position of bb with cc. Since the Huffman algorithm guarantees that the leaf nodes with the smallest weights at each level are merged, after the swap, the weighted path length of the tree (WPL) will decrease. This contradiction shows that the assumption is false, so the two leaf nodes with the smallest weights must be the deepest leaf nodes.

Next, suppose that the two leaf nodes with the smallest weights are aa and bb, and that they have the same depth. If these two nodes are not siblings in an optimal prefix-code tree, suppose there are other nodes cc and dd that are siblings of aa and bb, respectively (suppose aa and cc are siblings, and bb and dd are siblings). We can merge aa and bb into one subtree.

  • If the sum of the weights of the merged aa and bb is less than the weight of cc or dd, we can merge the resulting subtree with the node having the larger weight (such as cc or dd) to form a new subtree, which will reduce the WPL.
  • If the sum of the weights of aa and bb is no less than the weights of cc and dd, we can directly adjust aa and bb to be siblings, with cc and dd as another pair of siblings; the WPL will not increase.

Therefore, after such an adjustment, optimality is not destroyed. The proof is complete.

Theorem

The prefix-code tree obtained by the Huffman algorithm is an optimal prefix-code tree.

Proof

We use mathematical induction to prove this theorem.

  • Base case: When the number of letters is n=2n = 2, it is obvious that directly combining the two letters into one tree gives an optimal coding tree.
  • Inductive hypothesis: Suppose that when the number of letters is n=kn = k(k≥2k \geq 2), the Huffman algorithm can obtain an optimal prefix-code tree.
  • Inductive step: For n=k+1n = k + 1 letters, select the two letters with the smallest weights from the k+1k+1 letters and merge them into a subtree, using the root of the subtree as a virtual letter (virtual node). According to the lemma, this operation does not destroy the optimality of the prefix-code tree. At this point, the virtual letter and the remaining kk letters together constitute k+1k + 1 letters; by the inductive hypothesis, when the number of letters is kk, the Huffman algorithm can obtain an optimal prefix-code tree.

Therefore, by mathematical induction, the Huffman algorithm can obtain an optimal prefix-code tree for any number of letters nn. The proof is complete.

Huffman Coding

When designing programs, a separate code is usually assigned to each character to represent a set of characters; this is called coding.

When performing binary coding, suppose all codes have the same length. Then representing nn different characters requires ⌈log⁡2n⌉\left \lceil \log_2 n \right \rceil bits, which is called fixed-length coding.

If every character has the same frequency of use, fixed-length coding is undoubtedly the most space-efficient coding method. If characters occur with different frequencies, however, we can assign as short a code as possible to high-frequency characters and as long a code as possible to low-frequency characters to construct a kind of variable-length coding, thereby achieving better space efficiency.

When designing variable-length codes, the uniqueness of decoding must be considered. If no code in a set of codes is a prefix of any other code, then the set is called a prefix code, which guarantees the uniqueness of decoding.

A Huffman tree can be used to construct the shortest prefix code, namely the Huffman code (Huffman Code). Its construction steps are as follows:

  1. Suppose the character set to be encoded is: d1,d2,…,dnd_1,d_2,\dots,d_n, and their frequencies of occurrence in the string are: w1,w2,…,wnw_1,w_2,\dots,w_n.
  2. Use d1,d2,…,dnd_1,d_2,\dots,d_n as the leaf nodes and w1,w2,…,wnw_1,w_2,\dots,w_n as the leaf-node weights to construct a Huffman tree.
  3. Specify that the left branch of the Huffman coding tree represents 00 and the right branch represents 11. Then the sequence of 00s and 11s along the path from the root to each leaf node is the code of the character corresponding to that leaf node.
Figure: huffman tree 3

Example Code

Constructing a Huffman Tree
struct HNode {
  int weight;
  HNode *lchild, *rchild;
};

using Htree = HNode *;

Htree createHuffmanTree(int arr[], int n) {
  Htree forest[N];
  Htree root = NULL;
  for (int i = 0; i < n; i++) {  // Store all nodes in the forest
    Htree temp;
    temp = (Htree)malloc(sizeof(HNode));
    temp->weight = arr[i];
    temp->lchild = temp->rchild = NULL;
    forest[i] = temp;
  }

  for (int i = 1; i < n; i++) {  // Build the Huffman tree in n-1 iterations
    int minn = -1, minnSub;  // minn is the index of the root of the minimum-weight tree; minnSub is the index of the root of the second-smallest tree
    for (int j = 0; j < n; j++) {
      if (forest[j] != NULL && minn == -1) {
        minn = j;
        continue;
      }
      if (forest[j] != NULL) {
        minnSub = j;
        break;
      }
    }

    for (int j = minnSub; j < n; j++) {  // Assign values to minn and minnSub
      if (forest[j] != NULL) {
        if (forest[j]->weight < forest[minn]->weight) {
          minnSub = minn;
          minn = j;
        } else if (forest[j]->weight < forest[minnSub]->weight) {
          minnSub = j;
        }
      }
    }

    // Build a new tree
    root = (Htree)malloc(sizeof(HNode));
    root->weight = forest[minn]->weight + forest[minnSub]->weight;
    root->lchild = forest[minn];
    root->rchild = forest[minnSub];

    forest[minn] = root;     // Assign the pointer to the new tree to position minn
    forest[minnSub] = NULL;  // Clear position minnSub
  }
  return root;
}
Calculating the WPL of a Huffman Tree
struct HNode {
  int weight;
  HNode *lchild, *rchild;
};

using Htree = HNode *;

int getWPL(Htree root, int len) {  // Recursively calculate the WPL of an already constructed Huffman tree
  if (root == NULL)
    return 0;
  else {
    if (root->lchild == NULL && root->rchild == NULL)  // Leaf node
      return root->weight * len;
    else {
      int left = getWPL(root->lchild, len + 1);
      int right = getWPL(root->rchild, len + 1);
      return left + right;
    }
  }
}
Directly Calculating the WPL for an Unconstructed Huffman Tree
int getWPL(int arr[], int n) {  // Directly calculate the WPL for an unconstructed Huffman tree
  priority_queue<int, vector<int>, greater<int>> huffman;  // Min-heap
  for (int i = 0; i < n; i++) huffman.push(arr[i]);

  int res = 0;
  for (int i = 0; i < n - 1; i++) {
    int x = huffman.top();
    huffman.pop();
    int y = huffman.top();
    huffman.pop();
    int temp = x + y;
    res += temp;
    huffman.push(temp);
  }
  return res;
}
Calculating Huffman Codes for a Given Sequence
struct HNode {
  int weight;
  HNode *lchild, *rchild;
};

using Htree = HNode *;

void huffmanCoding(Htree root, int len, int arr[]) {  // Calculate Huffman codes
  if (root != NULL) {
    if (root->lchild == NULL && root->rchild == NULL) {
      printf("The code for the character at node %d is: ", root->weight);
      for (int i = 0; i < len; i++) printf("%d", arr[i]);
      printf("\n");
    } else {
      arr[len] = 0;
      huffmanCoding(root->lchild, len + 1, arr);
      arr[len] = 1;
      huffmanCoding(root->rchild, len + 1, arr);
    }
  }
}

This content comes from OI Wiki: Huffman Tree. See the original for authors and revision history. AIOJ has adapted the formatting, in-site links, and resource paths.

Content is shared under CC BY-SA 4.0 and SATA license; additional terms are governed by the original notice. Thanks to the OI Wiki authors and contributors; per the SATA terms, please visit the OI Wiki repository and star it to show your support.