c语言数据结构-哈夫曼树

简介: c语言数据结构-哈夫曼树

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹)

目录

哈夫曼树的定义

构造哈夫曼树

编码过程


哈夫曼树的定义

假设有 m个权值 {𝒘1 ,𝒘 2,  ··· , 𝒘 m } 可以构造一棵含n个叶子结点的二叉树,每个叶子结点的权为𝒘 𝒊 ,则其中带权路径长度 WPL最下的二叉树称作最优二叉树或哈夫曼树  

路径:a→b的路径:a→ 𝒘 2 、 𝒘 2 → 𝒘 𝟐 、 𝒘 1 → 𝒘 3 、 𝒘 𝟒 → 𝐛

路径长度:a→b的路径长度:4

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

权:对实体的某个或某些属性的数值化描述

结点的带权路径长度为从该结点到树根之间的路径长度与结点上权的乘积

数的带权路径长度为树中所有叶子结点的带权路径长度之和

/** Huffman树结点 */
typedef struct haffNode {
    char data;                      //用来存放字符的数据域
    int weight;                     //权重
    struct haffNode* leftChild;    //左孩子
    struct haffNode* rightChild;   //右孩子
}HaffNode;
/**以顺序结构存储的树结点,依次构建编码解码的字符映射表 */
HaffNode node[MAX_SIZE];

构造哈夫曼树

1.根据给定的n个权值{w 1 ,w 2 ,……w n },构造n棵只有根结点的二叉树。

2.在森林中选取两棵根结点权值最小的树作左右子树,构造一棵新的二叉树,置新二叉树根

  结点权值为其左右子树根结点权值之和。

3.在森林中删除这两棵树,同时将新得到的二叉树加入森林中。

重复上述两步,直到只含一棵树为止,这棵树即哈夫曼树。

/** 用来保存左子树结点 */
HaffNode left[HALF_MAX];
/** 用来保存右子树结点 */
HaffNode right[HALF_MAX];
/** 冒泡排序:以权值大小降序 */
void SortHaffman(HaffNode* node, int length) 
{
    HaffNode tempNode;
    for (int i = 0; i < length - 1; i++)
    {
        for (int j = 0; j < length - i - 1; j++)
        {
            //权值小的结点后移
            if (node[j].weight < node[j + 1].weight)
            {
                tempNode = node[j];
                node[j] = node[j + 1];
                node[j + 1] = tempNode;
            }
        }
    }
}
/**
 * 构造赫夫曼树
 *  node 赫夫曼树的结点数组
 *  length 结点数组的长度
 */
void CreateHaffman(HaffNode* node, int length)
{
    if (length <= 1)
        return;                 //数组长度为1时结束递归
    SortHaffman(node, length);              //将结点数组按weight(权)从大到小排列
    //构建一个以末尾两个结点为左右子结点的父节点,该父节点的权值为两个子结点权值之和
    HaffNode parent;
    //因为排过序了,所以最后一个结点[length-1]的权值肯定最小
    left[length] = node[length - 1];        //权值第二小的结点在左
    right[length] = node[length - 2];       //权值最小的结点在右
    parent.weight = left[length].weight + right[length].weight; //父节点权值为左右结点权值之和
    parent.leftChild = &left[length];       //左子树
    parent.rightChild = &right[length];     //右子树
    //将倒数第二位替换为该父节点,长度-1,递归创建赫夫曼树
    node[length - 2] = parent;
    CreateHaffman(node, length - 1);
}

编码过程

分解接收的字符串:遇“0”向左,遇“1”向右;一旦到达叶子结点,则译出一个字符,反复由根触发,直到译码完成。

特点:每一码都不是另一码的前缀,绝对不会错译

/**
 * 编码过程(压缩过程)
 * @param node 结点数组
 * @param keepCode 返回存储编码后的字符数组
 * @param index 当前操作的字符数组下标
 */
void Coding(HaffNode* node, char* keepCode, int index) {
    if (!node) return;
    //处理叶结点
    if (node->leftChild == NULL || node->rightChild == NULL) {
        //给编码数组设置一个终止符,形成一个完整的字符串,方便拷贝(防止拷贝到之前的编码)
        keepCode[index] = '\0';
        //node->data-0是char型转int型的简便方法
        strcpy(code[node->data - 0], keepCode);
        return;
    }
    //左分支编码为'0', 右分支编码为'1'
    keepCode[index] = '0';
    Coding(node->leftChild, keepCode, index + 1);
    keepCode[index] = '1';
    Coding(node->rightChild, keepCode, index + 1);
}


相关文章
|
7天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
72 9
|
6天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
46 16
|
6天前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
35 8
|
9天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
35 4
|
10天前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
34 3
|
10天前
|
存储 算法 C语言
C语言数据结构(2)
【10月更文挑战第21天】
|
9天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
26 0
|
1天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
4天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
6天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
27 4