【数据结构X.11】代码实现 哈夫曼树的创建,建立,构造,实现哈夫曼编码

32 篇文章 0 订阅
16 篇文章 1 订阅

主题

代码实现哈夫曼树的创建,建立,构造,实现哈夫曼编码,实现思路和要点:
抓住哈夫曼树的性质,每轮选出2个权值最小的点进行构造树。
抓住哈夫曼编码的性质,从根出发,向左标0,向右标1。

各种语言的实现可以看下面这个:
哈夫曼树的各种语言java,python,c++,c,c#的实现

哈夫曼树的构造思路:

网上说得都挺复杂的,看了半天还是看不懂,所以自己写了个:

  1. 每轮选出2个权值最小的点,权值加和后,放到空的新位置,标注左孩子和右孩子节点。例如这里,第一轮中,d和e点最小,权值的和为5,于是把空位置6更新为权值为5,左孩子d右孩子e。
  2. 一直这样挑选,直到所有的点都被挑选过used=1,只剩下一个点没有被used
  3. 利用这个得到的表去构造哈夫曼树。这个过程其实可以放到一个哈夫曼node集数组中,用for循环,从0到nodeNum遍历,一边遍历,一遍把左右节点连接上,最后返回Node[NodeNum-1],也就是这个没有被used过的节点。显然,它是根节点。这个其实还挺巧妙的,从这个题得到的启发:【数据结构X.8】代码实现和算法解析 已知一颗树的层次序列及每个节点的度, 编写算法构造这个树的孩子兄弟链表
  4. 最后进行编码。由于树的非递归后序遍历具有能保存其全部根节点的性质,这是因为进行后序遍历时,最后才会访问祖先节点,所以,当访问x节点时,其所有的祖先节点都存在栈里。利用这个性质,用栈保存从祖先节点,到X节点的路径,然后访问栈里的祖先节点,如果是左子树,标0,如果是右子树,就标1
    在这里插入图片描述

注意:

同一组节点,可以组成多个不同形状的哈夫曼树,他们的共同点是WPL值相同
构造1

哈夫曼树可以有不同的构造,例如下面的:
他们的WPL值都相同
W P L 值 = 路 径 长 度 ∗ 权 重 WPL值=路径长度*权重 WPL=都是 23 ∗ 2 + 11 ∗ 3 + 5 ∗ 4 + 3 ∗ 4 + 2 ∗ 29 + 3 ∗ 14 + 4 ∗ 7 + 8 ∗ 4 = 271 23*2+11*3+5*4+3*4+2*29+3*14+4*7+8*4=271 232+113+54+34+229+314+47+84=271
仅仅是左右换了下顺序
在这里插入图片描述

在这里插入图片描述

构造2

这里虽然形状改变了:
但是 路 径 长 度 ∗ 权 重 路径长度*权重 没有改变,依然满足前缀码的性质,也是哈夫曼树。
5 ∗ 5 + 29 ∗ 2 + 7 ∗ 4 + 8 ∗ 3 + 14 ∗ 3 + 23 ∗ 2 + 3 ∗ 5 + 11 ∗ 3 = 271 5*5+29*2+7*4+8*3+14*3+23*2+3*5+11*3=271 55+292+74+83+143+232+35+113=271
在这里插入图片描述

在这里插入图片描述

代码实现:

函数调用:
#include "HalfManTree.h"

int main()
{
    int num = 8;
    int weight[num] = {5, 29, 7, 8, 14, 23, 3, 11};
    //int weight[num] = {10, 5, 4, 2, 3, 6};
    char data[num] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
    HalfManItem item[MAX_SIZE];

    for (int i = 0; i < MAX_SIZE; i++)
    {
        item[i].data = 0;
        item[i].weight = 0;
        item[i].lchild = item[i].rchild = 0;
        item[i].used = false;
    }
    HalfManItem *ht = CreateHalfTable(item, data, weight, num);
   
    //PrintHalfManTable(ht, num);
    HalfManNode *tr = CreateHalfManTree(ht, num);
    //VisitHalfManTree(tr);
    char x = 'a';
    std::cout << "\na: ";
    GetHalfManCode(tr, x);
    std::cout << "\nb: ";
    GetHalfManCode(tr, 'b');
    std::cout << "\nc: ";
    GetHalfManCode(tr, 'c');
    std::cout << "\nd: ";
    GetHalfManCode(tr, 'd');
    std::cout << "\ne: ";
    GetHalfManCode(tr, 'e');
    std::cout << "\nf: ";
    GetHalfManCode(tr, 'f');
    std::cout << "\ng: ";
    GetHalfManCode(tr, 'g');
    std::cout << "\nh: ";
    GetHalfManCode(tr, 'h');
    //GetHalfManCode(tr,'d');
}
函数定义:
#include <iostream>
#include <stack>
#include <queue>
#include <malloc.h>
#include <string.h>
#define MAX_SIZE 200
#define MAX_INT 10000

//哈夫曼树的构造
typedef struct HalfManNode
{
    int freq; //出现频率
    char ch;  //保存的字符
    HalfManNode *lchild, *rchild;
} HalfManNode;

typedef struct HalfManItem
{
    char data;          //保存字符
    bool used;          //是否已经加入树了
    int weight;         //权重
    int lchild, rchild; //左孩子、右孩子序号
};

bool Select2Min(HalfManItem *ht, int &nodeNum);
HalfManItem *CreateHalfTable(HalfManItem *item, char data[], int weight[], int &nodeNum);

/***
 * 创建哈夫曼树
 * 输入 : 数据和权重,总节点数目
 */
HalfManItem *CreateHalfTable(HalfManItem *item, char data[], int weight[], int &nodeNum)
{
    for (int i = 0; i < nodeNum; i++)
    {
        item[i].data = data[i];
        item[i].weight = weight[i];
        item[i].lchild = item[i].rchild = -1;
        item[i].used = false;
    }

    int parentNum = nodeNum - 1;
    //由于有N个节点,每次插入都选择两个根节点权值最小的树作为新节点左右子树,并且创建一个新节点
    //根据哈夫曼树的性质,会新建N-1个新节点,一共需要选N-1次
    for (int i = nodeNum; i < nodeNum + parentNum; i++)
    {
        if (Select2Min(item, nodeNum))
        {
            std::cout << "成功新建节点,左孩子:" << item[nodeNum - 1].lchild << " 右孩子: "
                      << item[nodeNum - 1].rchild << " 权重:" << item[nodeNum - 1].weight << std::endl;
        }
        else
        {
            return item;
        }
    }
    return item;
}

/**
 * 选择哈夫曼树的两个最小的节点
 * 输入:
 * HalfManTree 树的根节点
 * n:节点数量
 * *s1\*s2: 
 */
bool Select2Min(HalfManItem *ht, int &nodeNum)
{
    int min1 = MAX_INT, min2 = MAX_INT;
    int minId1 = 0, minId2 = 0;

    HalfManItem *p = ht;
    int i = 0;
    for (; p < ht + nodeNum; p++)
    {
        if (!p->used && p->weight < min1)
        {
            min2 = min1;
            minId2 = minId1;
            min1 = p->weight;
            minId1 = i;
        } //小于min1,min1更新了,min2也要更新
        else if (!p->used && p->weight < min2)
        {
            min2 = p->weight;
            minId2 = i;
        }
        //比min1大,但是比min2小的情况
        i++;
    }
    if (min2 == MAX_INT)
    {
        return false;
    }
    //std::cout << min1<<"|"<<min2<<std::endl;
    (ht + nodeNum)->weight = min1 + min2;
    (ht + nodeNum)->data = ' ';
    (ht + nodeNum)->lchild = minId1;
    (ht + nodeNum)->rchild = minId2;
    (ht + nodeNum)->used = false;
    (ht + minId1)->used = true;
    (ht + minId2)->used = true;
    nodeNum += 1;
    return true;
}

/**
 * 打印二叉树全部节点
 */
void PrintHalfManTable(HalfManItem table[], int nodeNum)
{
    //std::cout << *table<<"  ---- ";
    for (int i = 0; i < nodeNum; i++)
    {
        std::cout << table[i].data << ": 左孩子:" << table[i].lchild << " 右孩子: "
                  << table[i].rchild << " 权重:" << table[i].weight << std::endl;
    }
    return;
}

/***
 * 创建哈夫曼树,用一个数组保存一堆哈夫曼节点,最后返回根节点
 */
HalfManNode *CreateHalfManTree(HalfManItem *ht, int nodeNum)
{
    HalfManNode *halfTree[nodeNum];
    for (int i = 0; i < nodeNum; i++)
    {
        halfTree[i] = (HalfManNode *)malloc(sizeof(HalfManNode));
        std::cout << "访问节点" << ht[i].data << " 权重:" << ht[i].weight << " lchild:"
                  << ht[i].lchild << " rchild:" << ht[i].rchild << std::endl;
        halfTree[i]->ch = ht[i].data;
        halfTree[i]->freq = ht[i].weight;
        if (ht[i].lchild != -1)
        {
            halfTree[i]->lchild = halfTree[ht[i].lchild];
            //std::cout << "当前节点:" << halfTree[i]->ch << " "<< "左孩子:" << halfTree[i]->lchild->ch;
        }
        else
        {
            halfTree[i]->lchild = NULL;
        }
        if (ht[i].rchild != -1)
        {
            halfTree[i]->rchild = halfTree[ht[i].rchild];
            //std::cout << "右孩子:" << halfTree[i]->rchild->ch << std::endl;
        }
        else
        {
            halfTree[i]->rchild = NULL;
        }

        //std::cout << "访问节点" << halfTree[i]->ch << " 权重:" << halfTree[i]->freq << " " << std::endl;
    }

    return halfTree[nodeNum - 1];
}

/***
 * 访问一下哈夫曼树
 */
void VisitHalfManTree(HalfManNode *root)
{
    if (root)
    {
        std::cout << "访问节点" << root->ch << " 权重: " << root->freq << " " << std::endl;
        VisitHalfManTree(root->lchild);
        VisitHalfManTree(root->rchild);
    }
}

/***
 * 已有哈夫曼树,获得哈夫曼编码
 * 
 */
void GetHalfManCode(HalfManNode *tr, char ch)
{
    std::stack<HalfManNode *> s;
    HalfManNode *p = tr, *r = NULL;
    while (!s.empty() || p)
    {
        if (p)
        {
            s.push(p);
            p = p->lchild;
        }
        else
        {
            p = s.top();
            if (p->rchild && p->rchild != r)
            { //p有右孩子,且没有被访问过
                p = p->rchild;
                s.push(p);
                p = p->lchild;
            }
            else
            {
                //std::cout << p->freq << " ";
                //std::cout << p->ch<<" "<<ch << " "<<std::endl;
                if (ch == p->ch)
                {
                    std::stack<HalfManNode *> s1;
                    while (!s.empty())
                    {
                        s1.push(s.top());
                        s.pop();
                    }
                    while (!s1.empty())
                    {
                        p = s1.top();
                        s1.pop();
                        if (!s1.empty())
                        {
                            if (s1.top() == p->lchild)
                            {
                                std::cout << "0";
                            }
                            else if (s1.top() == p->rchild)
                            {
                                std::cout << "1";
                            }
                            else
                            {
                                std::cout << "error";
                            }
                        }
                        else
                        {
                            return;
                        }
                    }
                }
                s.pop();
                r = p;
                p = NULL;
            }
        }
    }
}

参考:

其实没怎么参考,感觉网上的都写的好复杂
哈夫曼树的c实现
哈夫曼树的各种语言java,python,c++,c,c#的实现
OBST(最优二叉搜索树)

题目:

已知某系统在通信联络中只可能出现8种字符,其概率分别为0.05,0.29,0.07,0.08,0.14,0.23,0.03,0.11 试着设计赫夫曼编码。
假设权w=(5,29,7,8,14,23,3,11),n=8,则m=15,按照上述算法构造一颗哈夫曼树
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值