21. 哈夫曼树

一、什么是哈夫曼树

  哈夫曼树(Huffman Tree),又称为 最优二叉树最小带权路径长度树,是一种特殊的二叉树。假设二叉树有 n 个叶子结点,每个叶子结点带有权值 \(w_{k}\),从根结点到每个叶子结点的长度为 \(l_{k}\),则每个叶子结点的 带权路径长度(WPL) 之和就是:\(\sum_{k = 1}^{n}{w_{k}l_{k}}\)

哈夫曼树

二、哈夫曼树的构造

  1. 从小到大进行排序,每一个数据都可以看作一个结点,每个结点可以看作一个最简单的二叉树。
  2. 取出根结点权值最小的两颗二叉树
  3. 组成一颗新的二叉树,该二叉树的根结点的权值就是前面两颗二叉树根结点权值的和。
  4. 再将这个新的二叉树,一根结点的权值大小进行排序,不断重复步骤 1 ~ 4,直到数列中,所有的数据都被处理完。

哈夫曼树的构造

2.1、最小堆的构建

typedef struct HeapStruct
{
    TreeNode * Node;
    int Size;
    int Capacity;
} HeapStruct, * MinHeap;
#include <stdlib.h>
#include <limits.h>

/**
 * @brief 最小堆的创建
 * 
 * @param MaxSize 最大容量
 * @return MaxHeap 执行最小堆的指针
 */
MinHeap CreateMinHeap(int MaxSize)
{
    MinHeap H = (MinHeap)malloc(sizeof(HeapStruct));
    H->Node = (TreeNode *)calloc(sizeof(TreeNode), MaxSize + 1);
    H->Size = 0;
    H->Capacity = MaxSize;

    // 定义哨兵为小于堆中所有可能元素的值,便于后序操作
    H->Node[0].Weight = INT_MIN;
    H->Node[0].Data = '\0';
    H->Node[0].Left = H->Node[0].Right = NULL;

    return H;
}
/**
 * @brief 插入元素
 * 
 * @param H 最小堆
 * @param X 插入的元素
 */
void Insert(MinHeap H, TreeNode * X)
{
    int i = 0;
  
    if (H->Size == H->Capacity)
    {
        printf("堆已满,无法插入元素\n");
        return;
    }

    // i指向插入后堆中的最后一个元素的位置
    i = ++H->Size;
    // 从最后一个元素开始,依次向上比较,直到找到合适位置
    for (; H->Node[i / 2].Weight > X->Weight; i /= 2)
    {
        H->Node[i] = H->Node[i / 2];
    }
    // 插入元素,同类型结构体之间可直接赋值操作
    H->Node[i] = * X;
}
/**
 * @brief 删除元素
 * 
 * @param H 最小堆
 * @return ElementType 最大的元素
 */
TreeNode * Delete(MinHeap H)
{
    int Parent = 0, Child = 0;
    TreeNode * MinItem = NULL, temp = {0};

    if (H->Capacity == 0)
    {
        printf("堆为空,无法删除元素\n");
        return NULL;
    }

    MinItem = (TreeNode *)malloc(sizeof(TreeNode));
    MinItem->Weight = H->Node[1].Weight;
    MinItem->Left = H->Node[1].Left;
    MinItem->Right = H->Node[1].Right;

    // 用最小堆中最后一个元素从根结点开始向下过滤下层结点
    temp = H->Node[H->Size--];
    // Parent指向当前结点,Parent * 2 指向左子结点
    for (Parent = 1; Parent * 2 <= H->Size ; Parent = Child)
    {
        Child = Parent * 2;
        // Child != H->Size,意味着有右子结点,Child指向左右子结点的较小者
        if (Child != H->Size && (H->Node[Child].Weight > H->Node[Child + 1].Weight))
        {
            Child++;
        }
        if (temp.Weight <= H->Node[Child].Weight)
        {
            break;                                                              // 找到合适位置,退出循环
        }
        else
        {
            H->Node[Parent] = H->Node[Child];                                   // 移动temp元素到下一层
        }
    }

    H->Node[Parent] = temp;
  
    return MinItem;
}

2.2、哈夫曼树的构造

typedef struct TreeNode
{
    int Weight;
    struct TreeNode * Left;
    struct TreeNode * Right;
} TreeNode, * HuffmanTree, * Position;
/**
 * @brief 构造哈夫曼树
 * 
 * @param Data 数据序列
 * @param Length 数据序列的长度
 * @return HuffmanTree 指向哈夫曼树的指针
 */
HuffmanTree CreateHuffmanTree(ElementType Data[], int Length)
{
    int i = 0;
    HuffmanTree T = NULL;

    MinHeap H = CreateMinHeap(Length);
    for (int i = 0; i < Length; i++)
    {
        T = (TreeNode *)malloc(sizeof(TreeNode));                              // 创建新结点
        T->Weight = Data[i];
        T->Left = T->Right = NULL;
        Insert(H, T);                                                           // 将元素加入到最小堆
    }

    for (int i = 1; i < Length; i++)                                            // 做Length-1次合并
    {
        T = (HuffmanTree)malloc(sizeof(TreeNode));                              // 创建新结点
        T->Left = Delete(H);                                                    // 从最小堆中删除一个结点,作为新T的左子节点
        T->Right = Delete(H);                                                   // 从最小堆中删除一个结点,作为新T的右子节点
        T->Weight = T->Left->Weight + T->Right->Weight;                         // 新T的权值为左右子节点的权值之和
        Insert(H, T);
    }
  
    T = Delete(H);
  
    return T;
}
posted @ 2023-07-25 19:42  星光樱梦  阅读(28)  评论(0编辑  收藏  举报