数据结构与算法基础(王卓)(22):哈夫曼树

目录

前置条件:(哈夫曼树结构构造)

哈夫曼树构造(创建)过程:

基本核心流程(原理):

Part 1:

(1):2n和2 * n 完全不一样

(2):这里连续赋值的语句影响程序的最终结果吗

Part 2:

第一版:

Check过标准答案以后发现的问题:

最终版:

Select函数:

个人版本(我写的版本):

当然了,其中筛选判断语句是否执行的部分也可以更改成:

找到的一些标准答案的写法:(遍历思路结构有一些不同)

所以说,除了算法复杂度的问题之外,不用他这里赋初值的方式而是像我一样直接放第一个值是否可行???

最终成品:


前置条件:(哈夫曼树结构构造)

#include<iostream>
using namespace std;

struct HTNode
{
    int weight;
    int parent;
    int lchild, rchild;
};
typedef HTNode* HuffmanTree;
HuffmanTree HT;//既表示指针又表示整个数组

typedef int Status;

哈夫曼树构造(创建)过程:

基本核心流程(原理):

1、构造森林全是根;

2、选用两小造新树;

3、删除两小添新人;

4、重复 2、3 剩单根


Part 1:

void CreateHuffmanTree(HuffmanTree& H, int n) //已知输入有n个结点
{ 
    //1、构造森林全是根
    int m = 2 * n - 1;
    //数组共2n-1个有效元素
    H = new HTNode[m + 1];
    //第0位空置
    for (int i = 1; i <= m ; i++)
        H[i].lchild = H[i].parent = H[i].rchild = 0;
    for (int i = 1; i <= n; i++)
        cin >> H[i].weight;

这里中间犯过的错误我就不一一赘述了,只拿出其中几个踩过的坑来说一下:

(1):2n和2 * n 完全不一样

该问题来源自:    int m = 2 * n - 1;

猜测:(不过应该也是对的)

2n应该表示一个单独的(定义)变量

 2 * n应该才是表示两倍的 n 相乘的结果

(2):这里连续赋值的语句影响程序的最终结果吗

应该不影响,程序执行的操作也就只是从右往左一个一个赋值而已,这里并不影响操作结果:


Part 2:

第一版:

    //   2、选用两小造新树;
    //   3、删除两小添新人;
    //   4、重复 2、3 剩单根
    int s1, s2;
    for (; n <= m; n++)
    {
        Select(H, n - 1, s1, s2);
        H[n].weight = H[s1].weight + H[s2].weight;
        H[s1].parent = H[s2].parent = n;
        //如何限制让这两个已经被取出来使用过的元素在后面的循环不再被使用:
        //在Select函数里面添加限制条件:
        //我们只筛选比较选中范围内parent属性为0的元素
        H[n].lchild = s1;
        H[n].rchild = s2;   
    }

Check过标准答案以后发现的问题:

(1):我们新的元素应该从(n+1)开始

(2):必须要新设定一个新的变量 i 来记录后续遍历的整个过程而不能直接通字母n吗?

我觉得:(不一定对哈,还需要验证)

这里这个问题其实是个伪命题,因为我们即使按照你这里的问题写的,写成:

    for (int n = n + 1; n <= m; n++)

最终的结果其实也是:

我们构造了一个新的变量

更为直接的来说:此 n 非彼 n

这里这个语句里面定义的 n 也只是一个:有效范围为该for循环语句的 一个内部的局部变量

(3):同样的,这里的连续赋值影响最终结果吗??

和上面一样(同理),是不影响的


最终版:

    //   2、选用两小造新树;
    //   3、删除两小添新人;
    //   4、重复 2、3 剩单根
    int s1, s2;
    for (int i = n + 1; i <= m; i++)
    {
        Select(H, i - 1, s1, s2);
        H[i].weight = H[s1].weight + H[s2].weight;
        H[s1].parent = H[s2].parent = i;
        //如何限制让这两个已经被取出来使用过的元素在后面的循环不再被使用:
        //在Select函数里面添加限制条件:
        //我们只筛选比较选中范围内parent属性为0的元素
        H[i].lchild = s1;
        H[i].rchild = s2;

此时程序还没有写完:


Select函数:


个人版本(我写的版本):

void Select(HuffmanTree HT,int n,int &r1,int &r2)
{  
    //筛选查找出当前:
    // 第一位到第n位当中:parent属性为0、weight最小的节点
    //并把他们的序号赋值给r1和r2返回

    r1 =r2= 1;
    for (int i = 1; i <= n; i++)
    {
        if (HT[i].parent == 0)
        {
            if (HT[i].weight <= HT[r1].weight)
            {
                r2 = r1;
                r1 = i;
            }
        }
    }
}

当然了,其中筛选判断语句是否执行的部分也可以更改成:

    for (int i = 1; i <= n; i++)
    {
        if (HT[i].parent == 0 && HT[i].weight <= HT[r1].weight)
        {
            r2 = r1;
            r1 = i;
        }
    }

当然,这里这种这样的写法是否还有漏洞还有待考证


找到的一些标准答案的写法:(遍历思路结构有一些不同)

void Select(HuffmanTree& HT, int n, int& s1, int& s2)
{
    /*找第一个最小值*/
    int min;//用来存放最小值
    for (int i = 1; i <= n; i++)
    {
        if (HT[i].parent == 0)
        {
            min = i;
            break;
            //注意:这里我们的 break语句,让我们直接跳出for语句
            //但是说实话,这里他这个语句的操作是什么意思(想干嘛)我属实是没看懂:
            //把前面的n个元素全部遍历一遍,把第一个parent等于0的元素下标赋值给min,然后直接退出循环?
            //那我说实话你这个操作除了给min了一个初始值,其他跟啥也都没干有什么区别嘛...
        }
    }
    for (int i = min + 1; i <= n; i++)
    {
        if (HT[i].parent == 0 && HT[i].weight < HT[min].weight)
            // 第一位到第n位当中:parent属性为0、weight最小的节点
            min = i;
    }
    s1 = min; //第一个最小值给s1

    /*找第二个最小值*/
    for (int i = 1; i <= n; i++)
    {
        if (HT[i].parent == 0 && i != s1)
        {
            min = i;
            break;
            //同样的:把第一个非s1、parent等于0的元素下标赋值给min,退出循环
        }
    }
    for (int i = min + 1; i <= n; i++)
    {
        if (HT[i].parent == 0 && HT[i].weight < HT[min].weight && i != s1)
            // 第一位到第n位当中:parent属性为0、weight最小、非s1的节点
            min = i;
    }
    s2 = min; //第二个最小值给s2
}

所以说,除了算法复杂度的问题之外,不用他这里赋初值的方式而是像我一样直接放第一个值是否可行???


最终成品:

#include<iostream>
using namespace std;

struct HTNode
{
    int weight;
    int parent;
    int lchild, rchild;
};
typedef HTNode* HuffmanTree;
HuffmanTree HT;//既表示指针又表示整个数组

typedef int Status;

void Select(HuffmanTree& HT, int n, int& s1, int& s2)
{
    /*找第一个最小值*/
    int min;//用来存放最小值
    for (int i = 1; i <= n; i++)
    {
        if (HT[i].parent == 0)
        {
            min = i;
            break;
            //注意:这里我们的 break语句,让我们直接跳出for语句
        }
    }
    for (int i = min + 1; i <= n; i++)
    {
        if (HT[i].parent == 0 && HT[i].weight < HT[min].weight)
            min = i;
    }
    s1 = min; //第一个最小值给s1

    /*找第二个最小值*/
    for (int i = 1; i <= n; i++)
    {
        if (HT[i].parent == 0 && i != s1)
        {
            min = i;
            break;
        }
    }
    for (int i = min + 1; i <= n; i++)
    {
        if (HT[i].parent == 0 && HT[i].weight < HT[min].weight && i != s1)
            min = i;
    }
    s2 = min; //第二个最小值给s2
}

void CreateHuffmanTree(HuffmanTree& H, int n) //已知输入有n个结点
{
    //1、构造森林全是根
    int m = 2 * n - 1;
    //数组共2n-1个有效元素
    H = new HTNode[m + 1];
    //第0位空置
    for (int i = 1; i <= m; i++)
        H[i].lchild = H[i].parent = H[i].rchild = 0;
    for (int i = 1; i <= n; i++)
        //        H[i].weight = w[i - 1];
        cin >> H[i].weight;
    //   2、选用两小造新树;
    //   3、删除两小添新人;
    //   4、重复 2、3 剩单根
    int s1, s2;
    for (int i = n + 1; i <= m; i++)
    {
        Select(H, i - 1, s1, s2);
        H[i].weight = H[s1].weight + H[s2].weight;
        H[s1].parent = H[s2].parent = i;
        //如何限制让这两个已经被取出来使用过的元素在后面的循环不再被使用:
        //在Select函数里面添加限制条件:
        //我们只筛选比较选中范围内parent属性为0的元素
        H[i].lchild = s1;
        H[i].rchild = s2;
    }
}

int main()
{

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值