c++代码实现哈夫曼树的创建、编码以及求WPL (顺序结构)

c++代码实现哈夫曼树的创建、编码以及求WPL (顺序结构)

文章目录

exercise problem

​ 构造哈夫曼树生产哈夫曼编码,并求出带权路径长度(WPL)。

在这里插入图片描述

code
#include <iostream>
#include <cstring>
#include <string>
using namespace std;

#define ElemType string
#define INF 0x3f3f3f3f
typedef char** HuffCode;

int N;

typedef struct huffNode {
    double weight;                 //权重
    int lchild, rchild, parent;    //左右子节点和父节点
} HTNode, *HuffTree;

//找出剩下节点中权值最小的两个节点下标,分别用s1和s2保存
void select(const HuffTree &HT, int n, int &s1, int &s2);

//翻转字符串
void reverseChars(char *chs, int len);

//HT:哈夫曼树,HC:哈夫曼编码,w:构造哈夫曼树节点的权值,n:构造哈夫曼树节点的个数
void createHT(HuffTree &HT, HuffCode &HC, double *w, int n);

//输出各元素对应的哈夫曼编码
void showHuffCode(ElemType data[], HuffCode HC);

//求带权路径长度
int getWPL(HuffTree &HT, int idx, int depth);

int main() {
    N = 15;  //共15个有效元素
    //第0个元素保留不用
    ElemType data[N] = {"0","the","of","a","to","and","in","that","he","is","at","on","for","His","are","be"};
    //第0个元素保留不用
    double w[N] = {0,1192, 677, 541, 518, 462, 450, 242, 195, 190, 181, 174, 157, 138, 124, 123};

    HuffTree HT;
    HuffCode HC;

    createHT(HT, HC, w, N);

    cout << "Huffcode:\n";
    showHuffCode(data, HC);

    printf("\n");

    cout << "The weighted path length of the HuffmanTree is " << getWPL(HT, N * 2 - 1, 0) << "\n\n";

    system("pause");
    return 0;
}

//找出数组中权值最小的两个节点下标,分别用s1和s2保存
void select(const HuffTree &HT, int n, int &s1, int &s2) {
    s1 = s2 = 0;
    double min1 = INF; //最小值
    double min2 = INF; //次小值

    for (int i = 1; i <= n; i++) {
        //筛选没有父节点的最小和次小权值下标
        if (HT[i].parent == 0) {
            //如果比最小值小
            if (HT[i].weight < min1) {
                //更新次小值
                min2 = min1;
                s2 = s1;
                //更新最小值
                min1 = HT[i].weight;
                s1 = i;
            }
            //如果大于等于最小值,且小于次小值
            else if ((HT[i].weight >= min1) && (HT[i].weight < min2)) {
                //只更新次小值
                min2 = HT[i].weight;
                s2 = i;
            }
        }
    }
}

//翻转字符串
void reverseChars(char *chs, int len) {
    for (int i = 0; i < len / 2; i++) {
        char temp = chs[i];
        chs[i] = chs[len-1-i];
        chs[len-1-i] = temp;
    }
}

//HT:哈夫曼树,HC:哈夫曼编码,w:构造哈夫曼树节点的权值,n:构造哈夫曼树节点的个数
void createHT(HuffTree &HT, HuffCode &HC, double *w, int n) {
    int s1, s2, m = 2 * n - 1;                  //m: n个节点构造的哈夫曼树是2n-1个节点
    char* code;                                 //暂存编码
    HT = new HTNode[m+1];                       //0单元未使用

    for (int i = 1; i <= n; i++) {
        //初始化前n个节点 (构造哈夫曼树的原始节点)
        HT[i] = {w[i], 0, 0, 0};
    }

    for (int i = n+1; i <= m; i++) {
        //初始化后n-1个节点 (创建最小两节点的父节点)
        HT[i] = {0, 0, 0, 0};
    }

    //构建哈夫曼树
    for (int i = n+1; i <= m; i++) {
        //找出前i-1个节点中权值最小的节点下标
        select(HT, i - 1, s1, s2);
        HT[s1].parent = i;
        HT[s2].parent = i;
        HT[i].lchild = s1;
        HT[i].rchild = s2;
        HT[i].weight = HT[s1].weight + HT[s2].weight;
    }

    //哈夫曼编码
    HC = new char*[n];
    //暂存编码,使用了0单元
    code = new char[n];
     
    
    for (int i = 1; i <= n; i++) {
        //k: 当前节点,用于求对应的编码(0或1), f: k的父节点, j: 记录编码在字符数组code的位置
        int k = i, f = HT[k].parent, j = 0;
        //从叶子扫描到根
        while (f != 0) {
            if (HT[f].lchild == k) {
                code[j] = '0';
            }
            else if (HT[f].rchild == k) {
                code[j] = '1';
            }
            k = HT[k].parent;
            f = HT[k].parent;
            j++;
        }
        //标记末尾位置
        code[j] = '\0';
        reverseChars(code, j);
        //将暂存编码转移到HC[]中存储
        HC[i] = new char[n];
        strcpy(HC[i], code);
    }
}

void showHuffCode(ElemType data[], HuffCode HC) {
    for (int i = 1; i <= N; i++) {
        cout << data[i] << ": " << HC[i] << "\n";
    }
}

int getWPL(HuffTree &HT, int idx, int depth) {
    //dfs遍历直到该节点是叶子节点
    if(HT[idx].lchild == 0 && HT[idx].rchild == 0) {
        return HT[idx].weight * depth;
    }
    return getWPL(HT, HT[idx].lchild, depth + 1) + getWPL(HT, HT[idx].rchild, depth + 1);
}
running result

res

summary
注意:
1. 在对code进行赋值,将要结束的时候加上code[j] = '\0';以标记末尾位置, 以'\0'结尾是char数组的特点
2. strcpy是cstring库的函数,只能操作char * 类型的数据
3. 在数据数组data[], 权值数组w[] 中0单元不使用
4. typedef char ** HuffCode;
	HuffCode 实际上是一个字符串数组, 相当于string[], 它的第i个元素代表下标为i的节点对应的哈夫曼编码
思考:
1. 为什么在data[], w[]中第0个元素保留不用?
	在初始化哈夫曼树时,会给节点赋值HT[i] = {w[i]或0, 0, 0, 0},以标记该节点没有孩子节点和头结点。
	在创建哈夫曼树时,有if(HT[f].lchild(或rchild) == k)的判断语句,如果使用-1而不是0进行初始化,则会导致数组下标越界(array subscript -1 is below array bounds of ...)。
	这个问题可以在判断语句中加上 -1的情况先进行判断,从而解决。
	
2. 为什么不在 struct huffNode 加入成员变量 ElemType elem 表示数据?
	可以加入,使得huffNode承载的信息更加具体。
	但我个人觉得,在创建哈夫曼树的过程中,并未使用到数据,而仅仅是根据权值。
	数据和权值对应着不同数组的相同下标,并不会影响到编码的使用。
	
3. 为什么 struct huffNode 中既要记录lchild、rchild,也要记录parent?
	在获取数据(叶子节点)的哈夫曼编码的过程,要从叶子节点往上遍历直至根节点,所以需要parent成员变量,以进行遍历操作
  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Quantum_Wu

一起加油呀ヾ(◍°∇°◍)ノ゙

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值