C++哈夫曼树+哈夫曼编码的实现(双完整版)

注释详解哈夫曼Tree和哈夫曼Code

  • 一、哈夫曼Tree
  • 二、哈夫曼Code

  本文是根据B站视频青岛大学 - 王卓老师的数据结构来实现的,涉及到哈夫曼Tree 和 哈夫曼Code的C++版完整实现,若有不足欢迎大佬斧正-(/▽\)
C++哈夫曼树+哈夫曼编码的实现(双完整版)_第1张图片

一、哈夫曼Tree

  具体理论请配合B站视频来学习,构造哈夫曼Tree主要的方法如下:
  第一步:构造森林全是根
  第二步:选用两小造新树
  第三步:删除两小添新人(parent设置为 n+1 到 2n-1 中的下标)
  第四步:重复2、3步剩单根
  话不多说,我们只需要记住这四步,把下面代码的框架敲熟了,就能运用自如了。
  PS:代码有详细注解 和 引导思考,喜欢的话可以收藏一波~

#include 
#include 
#include 
using namespace std;

typedef struct HFT
{
	int weight;
	int parent, LTree, RTree;
}HFT, *PHFT;

void Select(PHFT HFT, int n, int &s1, int &s2)    //选取权值最小的两个结点
{
    for(int i = 1; i < n; i++){         //初始化s1,s1的双亲为0
        if(HFT[i].parent == 0){     /**  思考:为什么不是 i <= n  **/
            s1 = i;					//解答:因为我们第 2n-1  个元素的parent是0
            break;                          /**  思考:为什么要加上 break  **/
        }
    }

    for(int i = 1; i < n; i++){         //s1为权值最小的下标
            if(HFT[i].parent == 0 && HFT[s1].weight > HFT[i].weight)
                s1 = i;
    }

    for(int j = 1; j < n; j++){         //初始化s2,s2的双亲为0
        if(HFT[j].parent == 0 && j != s1){
            s2 = j;
            break;
        }
    }

    for(int j = 1; j < n; j++){         //s2为另一个权值最小的下标
        if(HFT[j].parent == 0 && HFT[s2].weight > HFT[j].weight && j != s1)
            s2 = j;
    }
}

void initHFT(PHFT &H, int n)
{
	if(n <= 1) return;
	int m = 2*n - 1;            //数组共2n - 1个元素
	H = new HFT[m + 1];	//0号单元未用,H[m]表示根节点
    for(int i = 1; i <= m; i++){
        H[i].parent = 0;
        H[i].LTree = 0;
        H[i].RTree = 0;
    }
    cout << "please input the weight of nodes:" << endl;
	for(int i = 1; i <= n; i++)
        cin >> H[i].weight;
    cout << endl;
    for(int i = n + 1; i <= m; i++)         //产生的新结点要放在从n+1开始,一直到2n-1的位置
    {
        int s1, s2;
        Select(H, i, s1, s2);
        H[s1].parent = i;
        H[s2].parent = i;   //相当于从表F中删除s1, s2
        H[i].LTree = s1;
        H[i].RTree = s2;
        H[i].weight = H[s1].weight + H[s2].weight;
    }
}

void showHFT(PHFT &H, int n)
{
    cout << "index  weight  parent  LTree  RTree" << endl;
    cout << left;     //左对齐输出
    int m = 2*n - 1;
    for(int i = 1; i <= m; i++){
        cout << setw(5) << i << "  ";                       /** 思考: 为什么是setw(5) 和 setw(6)   **/
        cout << setw(6) << H[i].weight << "  ";     // 解答:当后面紧跟着的输出字段长度小于n的时候,在该字段前面用空格补齐;当输出字段长度大于n时,全部整体输出
        cout << setw(6) << H[i].parent << "  ";
        cout << setw(6) << H[i].LTree << "  ";
        cout << setw(6) << H[i].RTree << "  " << endl;
    }
}

int main()
{
    PHFT HFT;
    int n = 0;
    cout << "please input the number of nodes: ";
    cin >> n;
    initHFT(HFT, n);
    showHFT(HFT, n);
    system("pause");
	return 0;
}

  我们可以对应下面这张图来看代码,弄懂思路。
C++哈夫曼树+哈夫曼编码的实现(双完整版)_第2张图片
Input
please input the number of nodes: 7
please input the weight of nodes:
7
19
2
6
32
3
21

Output
index weight parent LTree RTree
1  7   0   0   0
2  19  11   0   0
3  2   8   0   0
4  6   9   0   0
5  32  12   0   0
6  3   8   0   0
7  21  12   0   0
8  5   9   3   6
9  11  10   8   4
10  18  11  1   9
11  37  13  10   2
12  53  13  7   5
13  90  0  11   12
  

二、哈夫曼Code

  该代码在实现哈夫曼编码核心算法时既使用了C++的string类来实现,也使用了C的方式实现。这是在学会构造哈夫曼树之后的进一步提升,在这里给需要提高的同学抛出一个思考问题,“C++如何处理模板类template实现自动根据用户输入的 weight 值类型来分配内存”。
在这里插入图片描述
  首先先看图,根据图来实现以下步骤(用char动态数组):
  第一步:构建哈夫曼树表、HC表(动态二维数组)、cd表(一维)
  第二步:一般规定左子树路径为0,右子树路径为1,按哈夫曼树表寻找parent结点直到为0。
  与第二步同时进行:先将临时cd表最后一个元素定为’\0’,创建临时结点记录当前处理的结点。
  第三步:将cd表值赋值给HC表,同时销毁cd表的临时内存。

在这里插入代码片

```cpp
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef struct HFT
{
    float weight;
    int parent, LTree, RTree;
    string name;
}HFT, *PHFT;

void Select(const PHFT& H, const int& n, int& s1, int& s2){
    for(int i = 1; i < n; i++){
        if(H[i].parent == 0){
            s1 = i;
            break;
        }
    }
    for(int i = 1; i < n; i++){
        if(H[i].parent == 0 && H[s1].weight > H[i].weight)
            s1 = i;
    }

    for(int j = 1; j < n; j++){
        if(H[j].parent == 0 && j != s1){
            s2 = j;
            break;
        }
    }
    for(int j = 1; j < n; j++){
        if(H[j].parent == 0 && H[s2].weight > H[j].weight && j != s1)
            s2 = j;
    }
}

void initHFC(PHFT& HT, const int& n)
{
    if(n <= 1) return;
    int m = 2*n - 1;
    HT = new HFT[m + 1];
    for(int i = 1; i <= m; i++){
        HT[i].parent = 0;
        HT[i].LTree = 0;
        HT[i].RTree = 0;
    }
    cout << "please input the weight of nodes and nodes' name as 0.23 A: " << endl;
    for(int i = 1; i <= n; i++){
        cin >> HT[i].weight >> HT[i].name;
    }
    cout << endl;
    for(int i = n+1; i <= m; i++){
        int s1, s2;
        Select(HT, i, s1, s2);
        HT[s1].parent = i;
        HT[s2].parent = i;
        HT[i].LTree = s1;
        HT[i].RTree = s2;
        HT[i].weight = HT[s1].weight + HT[s2].weight;
    }
}

/**      char 类型解决方案   */
void ch_CreateHFMcode(const PHFT& HT, char** HC, const int& n)
{
    char* temp = new char[n];
    temp[n-1] = '\0';
    int  start = 0, c = 0, father = 0;
    for(int i = 1; i <= n; i++)
    {
        start = n - 1;
        c = i;                      //记录正在处理的当前位置
        father = HT[i].parent;
        while(father != 0)   //从叶子节点向上回溯
        {                //回溯一次 start指向前一个位置一个
            if(HT[father].LTree == c) temp[--start] = '0';
            else temp[--start] = '1';
            c = father;             //当前位置移到父节点
            father = HT[father].parent;    //更新父节点,继续向上回溯
        }
        HC[i] = new char[n-start];            // 为第 i 个字符串编码分配空间
        strcpy(HC[i], &temp[start]);             // 将求得的编码从临时空间cd复制到HC的当前行中,strcpy遇到 \0 拷贝就会结束
    }
    delete temp;
}

/**   string 类型的解决方案   **/
void str_CreateHFMcode(PHFT& H, string *HC, const int& n)
{
    string temp;
    stack<string> st;		//利用栈实现上一个char类型strcpy的方法
    int cur = 0, father = 0;
    for(int i = 1; i <= n; i++){
        cur = i;
        father = H[i].parent;
        while(father != 0)
        {
            if(H[father].LTree == cur) st.push("0");
            else st.push("1");
            cur = father;
            father = H[father].parent;
        }
        while(!st.empty()){
            temp += st.top();
            st.pop();
        }
        HC[i] = temp;
        temp.erase();	//擦除内存
    }
}

void showdata(const PHFT& HFT, char** HC, const int& n){
    cout << "index  weight  parent  LTree  RTree" << endl;
    cout << left;
    int m = 2*n - 1;
    for(int i = 1; i <= m; i++){
        cout << setw(5) << i << "  ";
        cout << setw(6) << HFT[i].weight << "  ";
        cout << setw(6) << HFT[i].parent << "  ";
        cout << setw(6) << HFT[i].LTree << "  ";
        cout << setw(6) << HFT[i].RTree << "  " << endl;
    }
    cout << endl;

    cout << "Name  HFMCode" << endl;
    for(int i = 1; i <= n; i++){
        cout << setw(5) << HFT[i].name << "  ";
        cout << setw(7) << HC[i] << "  " << endl;
    }
}

int main()
{
    PHFT HFT;
    int n = 0;
    cout << "please intput the number of vertices: ";
    cin >> n;
    char** HC = new char*[n];
    initHFC(HFT, n);
    ch_CreateHFMcode(HFT, HC, n);
    showdata(HFT, HC, n);
    return 0;
}

Input
please intput the number of vertice: 7
what value do you want to give them?
0.4
0.3
0.15
0.05
0.04
0.03
0.03

Output
index HC[i]
1  0
2  10
3  110
4  11111
5  11110
6  11100
7  11101
  
路曼曼其修远兮,吾将上下而求索

你可能感兴趣的:(算法学习,算法,数据结构,c++,树结构)