哈夫曼树的构造及哈夫曼编码(两种方法)

需要注意的是:

strcpy函数原型:char* strcpy(char* des,const char* source)

两个参数都是地址类型的

注意类型转换,保证类型一致匹配。

在两种解法中,Select函数的编写是借鉴csdn大佬的思路,非个人原创。

第一种解法:

#include<iostream>
#include<string.h>
# define MAX 99999
using namespace std;
typedef struct HTree
{
    int weight;//权值
    int parent, lchild, rchild;
}HTNode, * HuffmanTree;//一个*表示一级指针
typedef char** HuffmanCode;//char**二级指针,就是有两个门牌号,
//哈夫曼编码就像是一个锯齿状的二维数组,对于这个数组的大小我们不知道,这就需要动态申请(不浪费空间,灵活)
//还有,数组中的每一个,存储的是一个哈夫曼编码,没一个哈夫曼编码的长度我们也不知道,这也需要动态申请(指针)
//所以设置为二级指针就很好了。我们通过第一个门牌号找到了所有编码的位置,通过第二个门牌号,
//找到了每一个编码的位置。最终可以准确地到每一个编码。
void Select(HuffmanTree ht, int n, int *s1, int *s2);
void CreateHuffmanTree(HuffmanTree & HT, int n);
void CreatHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n);

int m,i;
void Select(HuffmanTree ht, int n, int *s1, int *s2) 
{
    int i, min1 = MAX, min2 = MAX;//求最大最小的常用套路
    *s1 = 0;
    *s2 = 0;
    for (i = 1; i <= n; i++) 
    {
        if (ht[i].parent == 0) //双亲为0表明没有被创建过,代表是根节点。不是0表明被创建过,拥有双亲,是孩子节点。控制了选择范围。
        {
            if (ht[i].weight < min1) 
            {
                min2 = min1;
                *s2 = *s1;
                min1 = ht[i].weight;
                *s1 = i;
            }
            //这一段代码,先更新第二小,在更新第一小。因为在条件ht[i].weight < min1满足的时候,
            //就表明第一小要被更新。那么显然,原来的第一小就变成了第二小。
            //在更新第一小的时候,必须更新第二小。否则得不到第二小,或者得到的是错误的。
            //第一小和第二小是有关系的,二者在寻找过程中,是变动的,动态的,持续更新,直到确定。
            else if (ht[i].weight < min2) 
            {
                min2 = ht[i].weight;
                *s2 = i;
            }
        }
    }
}
//Select函数中最最重要的两行代码就是:min2 = min1;*s2 = *s1;
 //               
void CreateHuffmanTree(HuffmanTree &HT,int n)
{
    //执行树的构造的前提条件
    if (n < 1) return;
    m = 2 * n - 1;
    HT = new HTNode[m + 1];//m+1=2n个空间:0号单元不使用,n个叶子结点,n-1个新创建出来的结点。HT[m]是根节点
    //初始化,便于标记记录
    //刚开始,每个叶子节点都是独立的,都是自己的根,都没有双亲,没有孩子。所以 HT[i].parent = 0; HT[i].lchild = 0; HT[i].rchild = 0;
    for(i=1;i<=m;++i)
    {
        HT[i].parent = 0; HT[i].lchild = 0; HT[i].rchild = 0;
    }
    //输入数据(只有叶子节点)
    for(i=1;i<=n;++i)
    {
        cin >> HT[i].weight;
    }
    //对新节点进行创建,进行哈夫曼树的构造
    int s1, s2;
    //拥有n个叶子结点的哈夫曼树共有2*n-1个节点。2*n-1=n+(n-1)  前者n代表原来的叶子节点,n-1代表新构造出来的节点。
    //根据哈夫曼树的构造原则就可以知道,每次从现有的所有结点中挑选出权值最小的两个节点进行构造,得到一个新节点。
    //这个新结点是被挑选出的两个节点的双亲  ,被挑选的节点是这个节点的孩子。
    //第一种理解方法:第一次挑选的时候,原节点:n-2  新节点:1 (暂时不加入前者)  再进行第二次挑选的时候(仅仅只是记录结点的数目)
    //我们需要得到两个节点,通过第一次已经得到一个,所以每次我们只需要在得到一个节点即可(仅考虑数目)
    //那么剩余的n-2个节点,每次挑选一个,我们就需要挑选n-2次,再加上第一次,共挑选n-1次。
    for(i=n+1;i<=m;++i)//通过n-1次的选择,删除,合并来创建哈夫曼树。
    {
        Select(HT, i - 1, &s1, &s2);//其中第二个参数,是进行挑选的次数。i-1=n次??怎么不是n-1次?
        //后两个参数是我们需要从叶子节点中找到min1  min2并且返回他们的位置,要使用引用传递。以便进行操作。
        HT[s1].parent = i; HT[s2].parent = i;//双亲
        HT[i].lchild = s1; HT[i].rchild = s2;//孩子
        //所有节点共有2*n-1个,我们开辟了一个数组大小为2*n
        //(对于数组来说,你要时刻记住,如果数组大小为n,那么存储的下标范围是0~n-1,最大的存储下表是n-1)
        //0号单元不使用,1~n个单元存储的是叶子,n+1~2*n-1
        //每创建一个节点,就赋予他一个下标。n+1~2*n-1
        HT[i].weight = HT[s1].weight + HT[s2].weight;
    }
}
void CreatHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n)
{
    HC = new char* [n + 1];//指针数组  仅仅是门牌号,还不是存储数据的房间
    char* cd = new char[n];//临时空间:让最后一个编码先存放,从后面开始存放。
    //那么后半份空间被使用,前面的空间没有用。
    cd[n - 1] = '\0';//字符串结束标志
    for(i=1;i<=n;++i)//得到所有编码
    {
        int start = n - 1;
        int c = i;
        int f = HT[i].parent;
        //得到一个叶子的编码
        while (f != 0)//while的条件!!!
        {
            --start;
            if (HT[f].lchild == c) cd[start] = '0';
            else cd[start] = '1';
            c = f;//向上移动
            f = HT[f].parent;//向上移动
        }
        //将临时空间中的编码存入真正需要的空间
        HC[i] = new char[n - start];//申请存储空间
        strcpy(HC[i], &cd[start]);
    }
    //全部编码完成,删除不需要的申请空间
    delete cd;
}
int main()
{
    int n=5,i;
    HuffmanTree HTR = NULL;
    CreateHuffmanTree(HTR, 5);
    HuffmanCode hc = NULL;
    CreatHuffmanCode(HTR, hc, 5);
    cout<<"各结点的值:"<<endl;
    for(i=1;i<=9;i++)
    {
        cout<<HTR[i].weight<<" ";
    }
    cout<<endl;
    cout<<"编码:"<<endl;
    for(i=1;i<=5;i++)
    {
        cout << hc[i]<< endl;
    }
    return 0;
}

第二种解法:


//#pragma once
#include<iostream>
#include<string.h>
# define MAX 99999
using namespace std;
typedef struct HTree
{
    int weight;
    int parent, lchild, rchild;
}HTNode, * HuffmanTree;
typedef char** HuffmanCode;
void Select(HuffmanTree ht, int n, int *s1, int *s2);
void CreateHuffmanTree(HuffmanTree & HT, int n);
void CreatHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n);



int m,i;
void Select(HuffmanTree ht, int n, int &s1, int &s2) 
{
    int i, min1 = MAX, min2 = MAX;
    s1 = 0;
    s2 = 0;
    for (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 < min2) 
            {
                min2 = ht[i].weight;
                s2 = i;
            }
        }
    }
}
//笨方法,但是易于理解,得出的结果也没有错。
//思路:先遍历一次,找到最小的。再把最小的剔除。在遍历一次,找到此时的最小的(其实是第二小)
//这其中的重点就是如何剔除!!!!!不要忘了我们的目的,是为了找到两个最小的节点,进行树的创建。
void Select1(HuffmanTree ht, int n, int &s1, int &s2) 
{
    int i, min1 = MAX, min2 = MAX;
    s1 = 0;
    s2 = 0;
    for (i = 1; i <= n; i++) 
    {
        if (ht[i].parent == 0) 
        {
            if (ht[i].weight < min1) 
            {
                min1 = ht[i].weight;
                s1 = i;
            }
        }
    }
    for (i = 1; i <= n; i++) 
    {
        if (ht[i].parent == 0) 
        {
            if (ht[i].weight < min2&&i!=s1)// ht[i].weight !=min1 选取节点构造哈夫曼树,不是单纯找最小
            {
                min2 = ht[i].weight;
                s2= i;
            }
        }
    }
}

void CreateHuffmanTree(HuffmanTree &HT,int n)
{
    
    if (n < 1) return;
    m = 2 * n - 1;
    HT = new HTNode[m + 1];
    
    for(i=1;i<=m;++i)
    {
        HT[i].parent = 0; HT[i].lchild = 0; HT[i].rchild = 0;
    }
    
    for(i=1;i<=n;++i)
    {
        cin >> HT[i].weight;
    }
    //对新节点进行创建,进行哈夫曼树的构造
    int s1, s2;
    for(i=n+1;i<=m;++i)
    {
        Select1(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;
    }
}
void CreatHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n)
{
    HC = new char* [n + 1];
    char* cd = new char[n];
    
    cd[n - 1] = '\0';
    for(i=1;i<=n;++i)
    {
        int start = n - 1;
        int c = i;
        int f = HT[i].parent;
        //得到一个叶子的编码
        while (f != 0)
        {
            --start;
            if (HT[f].lchild == c) cd[start] = '0';
            else cd[start] = '1';
            c = f;//向上移动
            f = HT[f].parent;
        }
        
        HC[i] = new char[n - start];
        strcpy(HC[i], &cd[start]);
    }
    delete cd;
}

void printHtree(HuffmanTree ht)
{
    cout<<"各结点的值:"<<endl;
    for(i=1;i<=9;i++)
    {
        cout<<"No.:"<<i
        <<" wt:"<<ht[i].weight<<" parent:"<<ht[i].parent<<" rchild:"<<ht[i].rchild<<" lchild:"<<ht[i].lchild<<endl;;

    }
    cout<<endl;
   
}


void printHtreeCode(HuffmanCode hc)
{
     cout<<"编码:"<<endl;
    for(int i=1;i<=5;i++)
    {
        cout << hc[i]<< endl;
    }
}

int main()
{
    int n=5;
    HuffmanTree HTR=NULL;
    CreateHuffmanTree(HTR, 5);
    printHtree(HTR);
    HuffmanCode hc = NULL;

    CreatHuffmanCode(HTR, hc, 5);
    printHtreeCode(hc);
    return 0;
}

小新还在努力,文章中如有错误,请多多指教。

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值