首页 > 其他分享 >最优二叉树—哈夫曼(huffman)树

最优二叉树—哈夫曼(huffman)树

时间:2023-10-05 14:32:57浏览次数:38  
标签:huff 结点 哈夫曼 weight int 二叉树 huffman 节点

哈夫曼树又称最优二叉树,是一类带权路径长度最短的二叉树,有着广泛的应用。


基本概念

权:将树中的结点赋上一个有着某种意义的数值

路径:从A结点道B结点所经过的分支序列

路径长度:从A结点道B结点所经过的分支数目


查找效率

平均查找长度(ASL)取决于树的高度

最优二叉树—哈夫曼(huffman)树_权值最优二叉树—哈夫曼(huffman)树_权值_02

ASL=(1+2*2+3)/4=2            ASL=(1+2+3+4)/4=2.5

        O(log2n)                               O(n)     

          二叉树                               单链表 


带权路径长度  

路径长度:路径上所经历边的个数

结点的权:结点被赋予的数值

树的带权路径长度    WPL树中所有叶结点的带权路径长度之和,记为最优二叉树—哈夫曼(huffman)树_算法_03

最优二叉树—哈夫曼(huffman)树_算法_04

WPL=7*2+2*2+3*3=24                              WPL=7*1+2*2+3*2=17

哈夫曼树   也称最优二叉树,含有n个带权叶子节点带权路径长度最小的二叉树

带权路径长度:树的根节点A结点的路径长度与A结点上的权的乘积

树的路径长度:一棵树中从根节点到每个结点的路径长度之和

树的带权路径长度:树中所有叶子节点的带权路径长度之和最优二叉树—哈夫曼(huffman)树_权值_05

最优二叉树—哈夫曼(huffman)树_权值_06

WPL=2*2+2*4+2*5+2*8=38

WPL=4*2+5*3+8*3+2*1=49

WPL=8*1+5*2+4*3+2*3=36


哈夫曼树的构造算法


  • 将n个结点作为n棵仅含有一个根结点的二叉树,构成森林F
  • 生成一个新结点,井从F中找出根结点权值最小的两棵树作为它的左右子树,且新结点的权值为两棵子树根结点的权值之和
  • 从F中删除这两个树,并将新生成的树加入到F中
  • 重复2,3步骤,直到F中只有一棵树为止

最优二叉树—哈夫曼(huffman)树_结点_07


哈夫曼树的性质


  • 每个初始结点都会成为叶节点,双支结点都为新生成的结点
  • 权值越大离根结点越近,反之权值越小离根结点越远
  • 哈夫曼树中没有结点的度为1
  • n个叶子结点的哈夫曼树的结点总数为2n-1,其中度为2的结点数为n-1

最优二叉树—哈夫曼(huffman)树_结点_08


构造哈夫曼树的方法

哈夫曼树的形态不是唯一的,第二步中的左右子树位置并不一定是小在左大在右,但具有一组权值的哈夫曼树的WPL是唯一的


哈夫曼树的构造算法:

编码  对于一个字符串的序列,用二进制来表示字符

最优二叉树—哈夫曼(huffman)树_结点_09最优二叉树—哈夫曼(huffman)树_huffman树_10

哈夫曼编码

构造一个哈夫曼树

#include <stdio.h>
#include <stdlib.h>

#define MAX_N 1000

typedef struct _huff_node {
    int weight;
    int parent, lchild, rchild;
} HuffNode;

void huffman_tree(int n, int *weight, HuffNode *huff) {
    // 初始化哈夫曼树的n个叶子结点
    for (int i = 0; i < n; i++) {
        huff[i].weight = weight[i];
        huff[i].parent = -1;
        huff[i].lchild = -1;
        huff[i].rchild = -1;
    }

    // 构造哈夫曼树
    for (int i = n; i < 2 * n - 1; i++) {
        int min1 = -1, min2 = -1;
        for (int j = 0; j < i; j++) {
            if (huff[j].parent == -1) {
                if (min1 == -1 || huff[j].weight < huff[min1].weight) {
                    min2 = min1;
                    min1 = j;
                } else if (min2 == -1 || huff[j].weight < huff[min2].weight) {
                    min2 = j;
                }
            }
        }
        huff[min1].parent = i;
        huff[min2].parent = i;
        huff[i].lchild = min1;
        huff[i].rchild = min2;
        huff[i].weight = huff[min1].weight + huff[min2].weight;
    }
}

int main() {
    int n, weight[MAX_N];
    HuffNode huff[MAX_N * 2 - 1];

    // 读入叶子结点个数和权值数组
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &weight[i]);
    }

    // 构造哈夫曼树
    huffman_tree(n, weight, huff);

    // 输出哈夫曼树的结构
    for (int i = 0; i < 2 * n - 1; i++) {
        printf("Node %d: weight=%d, parent=%d, lchild=%d, rchild=%d\n",
                i, huff[i].weight, huff[i].parent, huff[i].lchild, huff[i].rchild);
    }

    return 0;
}

这个代码读入叶子结点个数和权值数组,然后构造哈夫曼树,并输出每个节点的权值、父节点、左子节点和右子节点。

代码解释:

  1. #include <stdio.h>#include <stdlib.h> 分别引入了标准输入输出库和标准库的头文件。
  2. #define MAX_N 1000 定义了一个宏,表示叶子结点的最大数量。
  3. typedef struct _huff_node {...} HuffNode; 定义了一个名为HuffNode的结构体,表示哈夫曼树中的节点。结构体包含了权值(weight)、父节点(parent)、左子节点(lchild)和右子节点(rchild)等字段。
  4. void huffman_tree(int n, int *weight, HuffNode *huff) {...} 是构造哈夫曼树的函数。参数包括叶子结点个数n、权值数组weight[]和存储哈夫曼树的数组huff[]。
  5. for (int i = 0; i < n; i++) {...} 初始化哈夫曼树的n个叶子结点。遍历每个叶子结点,设置其权值为weight[i],并将父节点、左子节点和右子节点初始化为-1。
  6. for (int i = n; i < 2 * n - 1; i++) {...} 构造哈夫曼树的非叶子结点。从第n个位置开始,遍历每个非叶子结点,选择权值最小的两个节点作为其左右子节点,并更新相关字段。
  7. int main() {...} 是主函数。在主函数中,首先声明了叶子结点个数n和权值数组weight[MAX_N],以及存储哈夫曼树的数组huff[MAX_N * 2 - 1]。
  8. scanf("%d", &n); 读入叶子结点的个数n。
  9. for (int i = 0; i < n; i++) {...} 通过循环读入叶子结点的权值数组weight[]。
  10. huffman_tree(n, weight, huff); 调用huffman_tree函数构造哈夫曼树。
  11. for (int i = 0; i < 2 * n - 1; i++) {...} 遍历哈夫曼树的所有结点,并输出每个结点的权值、父节点、左子节点和右子节点的信息。
  12. return 0; 表示程序正常结束。

总体来说,通过构造函数的方式实现了构造哈夫曼树的功能,并提供了一个简单的演示程序来展示结果。使用了结构体来存储每个节点的信息,并通过循环来遍历和处理每个节点,最终输出了哈夫曼树的结构信息。




标签:huff,结点,哈夫曼,weight,int,二叉树,huffman,节点
From: https://blog.51cto.com/wamtar/7713447

相关文章

  • 二叉树遍历(后序遍历)
    口诀:先左再右再根......
  • Madoka and The Corruption Scheme (CF D)(二叉树 整体考虑)
       思路:题意性质:要让某个人赢,从上往下右走了几次到他,因此就是从n轮中选择k次往右走的所有情况ans就是tot- C(n,i)i>k的选择次数,把大的数往里面赛就行了. ......
  • 根据先序序列和中序序列构造二叉树
    阅读本文之前希望读者可以先掌握如何根据先序序列和中序序列手动画出二叉树。所用二叉树数据结构如下:typedefstructTreeNode{ chardata; TreeNode*lchild,*rchild;}TreeNode,*Tree;该方法声明如下TreecreateTree(char*pre,intl1,intr1,char*in,intl2,intr2);......
  • 二叉树遍历(中序遍历)
    中序遍历,就是从最左边开始,把每个节点垂直投影到同一直线上,然后从左往右读值就可以了。口诀:先左再根再右......
  • 二叉树遍历(先序遍历)
    口诀:先根再左再右......
  • P1305 新二叉树
    Problem题目简述给你一个二叉树,求前序遍历。输入的方法为左右孩子表示法。思路这道题的话可以DFS。定义一个结构体\(node\),存储\(3\)个信息:\(fa,l,r\)分别表示父亲、左子树、右子树。然后下标就是字母的\(ACSII\)码。然后每次将左子树、右子树的\(fa\)更新,然后......
  • 【洛谷 P1305】新二叉树 题解(结构体数组+先序遍历+二叉树)
    新二叉树题目描述输入一串二叉树,输出其前序遍历。输入格式第一行为二叉树的节点数。()后面行,每一个字母为节点,后两个字母分别为其左右儿子。特别地,数据保证第一行读入的节点必为根节点。空节点用*表示输出格式二叉树的前序遍历。样例#1样例输入#16abcbdicj*d**i**j**......
  • 哈夫曼编码及例程
    哈夫曼编码是一种常见的无损压缩算法,通过根据字符出现的频率构建一个最优编码树,将频率较高的字符用较短的编码表示,从而实现数据的压缩。下面是一个简单的例程来演示如何使用哈夫曼编码进行文本数据的压缩和解压缩。压缩过程:统计输入文本中每个字符的出现频率。根据字符频率构建哈夫......
  • 226. 翻转二叉树
    给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。示例1:输入:root=[4,2,7,1,3,6,9]输出:[4,7,2,9,6,3,1]示例2:输入:root=[2,1,3]输出:[2,3,1]示例3:输入:root=[]输出:[]第一眼想到了递归/***Definitionforabinarytreenode.*structTreeNode{*......
  • 【UVA 536】Tree Recovery 题解(根据遍历序列还原二叉树)
    小瓦伦丁非常喜欢玩二叉树。她最喜欢的游戏是随机构建查找节点中带有大写字母的二叉树。这是她创作的一个例子:为了给后代记录她的树,她为每棵树写下了两个字符串:预订单遍历(根、左子树、右子树)和有序遍历(左子树、根、右子树。对于上面绘制的树,预序遍历是DBACEGF,有序遍历是ABCDEFG......