数据结构与算法基础(王卓)(21):哈夫曼编码(1):过程

逻辑雏形

根据老师讲解的思路,梳理出程序运行的逻辑雏形如下:

  1. 搞一个多维数组HC,用来存储我们这里 n(每) 个节点的哈夫曼编码
  2. 搞一个数组cd,用来存储我们这里每个节点是前面一位的左子树(0)还是右子树(1),给cd最后一位放结束存储字符
  3. 反复向上回溯:每次都通过找(parent)来判断自己是左子树还是右子树,并且在cd表里记录每次的查询结果,然后再继续向上回溯看上一层(parent),在进行同样(相同)的循环操作
  4. 给HC开辟相应的,每个节点对应需要的存储空间大小的空间
  5. 每算完一个节点的哈夫曼编码就把它放(复制)到HC里面
  6. 释放空间,结束

需要注意的是,由于我们的函数抬头是:

void CreatHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n)

也就是说我们在一开始的时候,就已经传进来了一颗哈夫曼树,所以我们这里在函数里面不用再重新重复算一遍哈夫曼树


第一步:

创建二维数组HC,同时也要确定(定义)下来【HuffmanCode】的类型是什么

typedef char **HuffmanCode;
//typedef char** HuffmanCode;

void CreatHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n)
{
    HC = new char *[n + 1];  
}

(1):这里,关于这个定义我们可以这样来理解:

HuffmanCode是一个内部内容为char类型的一个多维数组(这里应该是二维数组)

*(*HuffmanCode):表示二维数组的子集:

表示若干个一维数组,每个数组都是一个char类型的一个一维数组

**HuffmanCode:一个二维数组

由若干个char类型一维数组组成的一个二维数组

其中每一个一维数组又可以分为若干个char类型的元素/结点

另外,这里关于开辟空间new的格式使用规则,感觉还是有点不稳的可以直接看:

数据结构与算法基础(王卓)(8)附:关于new的使用方法详解_数据结构中的new_宇 -Yu的博客-CSDN博客


(2):为什么我们给他分配(n+1)个元素(格子)???

猜测:

可能是因为(第)一个用来放表头(头结点),然后后面还有(n)个节点


第二步:

给数组cd开辟空间,标准答案:

    char* cd = new char[n];  //分配存放临时存放编码的动态数组空间
    cd[n - 1] = '\0';

可是我觉得不对吧,为什么在数组倒数第二个位置上放空字符啊?

根据前面实际(操作)的案例,空字符不应该是放在最后一位吗??

数据结构与算法基础(王卓)(21):哈夫曼编码(1):过程_第1张图片

 


第三步:

生成哈夫曼编码

我写的:(第一版)

    for (int i = 1; i <= n; i++)
    {    
        int a = HT[i].parent;
        while (HT[a].parent != 0)
        {
            //往cd的最后面一位输入:左0右1
            if (HT[a].lchild == i)
            {
                for (int j = n; n >= 1; n--)//最后面一位空着的字符
                {
                    if (cd[n] != NULL)
                    {
                        cd[n - 1] = 0;
                        break;
                    }
                }
            }
            else
            {
                for (int j = n; n >= 1; n--)
                {
                    if (cd[n] != NULL)
                    {
                        cd[n - 1] = 1;
                        break;
                    }
                }
            }
        }

问题:如果像我这样写,最终会导致:

最后每次的输入都只输入了最后一位的数值,而且还把数组写满了

我写的程序里面的记录HT[i].parent的a,我一直没有对他进行任何操作

他没有任何变化,程序一直在重复写入第一位parent所对应的哈夫曼编码


标准答案:(这里把后面几步也写进去了)

    //逐字符求Huffman编码
    for (int i = 1; i <= n; i++)
    {
        int start = n - 1;
        //表示(记录)我们在cd表当中输入的每个分支(边)其对应的位置序号
        int c = i;
        int f = HT[i].parent;
        while (f != 0)
        {
            start--;
            //每回溯一次,start(写入cd表的信息位置)向前指一个位置
            if (HT[f].lchild == c)//左0右1
                cd[start] = '0';
            else  //是右孩子
                cd[start] = '1';
            //继续向上回溯
            c = f;
            f = HT[f].parent;
        }
        HC[i] = new char[n - start];  //为第i个字符串编码分配空间
        strcpy(HC[i], &cd[start]);  //将求得的编码复制
    }

两种做法思路不同:

我的思路:

  1. 每次都从cd表最后一位往前逐个查找,直至找到第一个空着的位置
  2. 往里面输入数据
  3. 输入完以后直接跳出查找,直接进入下一轮for(i)的循环

标准答案:

  1. 先设立一个变量s,表示(记录)每次cd表当中输入的分支(边)其对应的位置序号
  2. 每输入cd表1次,s就向前指一个位置
  3. 输入
  4. 把比较使用的结点改成parent的结点,然后进入下一轮的循环和比较

优于我的方法的地方:

  • 每次输入cd表时:
  • 设置变量a表示当前输入对应结点的位置
  • 设置变量b表示当前输入对应结点的parent的位置

但是(然而)这里对于标准答案,我也有一个

问题:

他你这个cd表,前面你在输入空字符的时候放在(n-1)的位置,怎么你第一次输入的时候还是放在(n-1)位?


根据标准答案修改以后的个人最终版本:

    for (int i = 1; i <= n; i++)
    {    
        int np = n;//now point
        int pp = HT[i].parent;//parent point
        int insert = n - 1;//inser:插入

        while (HT[pp].parent != 0)
        {
            //每输入cd表1次,下次输入就向前指一个位置
            insert--;

            //往cd的最后面一位输入:左0右1
            if (HT[pp].lchild == i)
                cd[n - 1] = 0;
            else
                cd[n - 1] = 1;

            //把比较使用的结点改成parent的结点,然后进入下一轮的循环和比较
            np = pp;
            pp = HT[np].parent;
        }

第四步:(这里我们就把最后收尾工作合并一起写了)

  1. 给HC开辟相应的,每个节点对应需要的存储空间大小的空间
  2. 每算完一个节点的哈夫曼编码就把它放(复制)到HC里面
  3. 释放空间,结束
        HC[i] = new char[n - insert];
        //copy H[i] from cd to HC;

这里,我们可以按标准答案:

使用 strcpy函数(虽然实际上我也没听说过),最终再用delete释放空间

当然这个过程中还是碰到了一些

问题:

数据结构与算法基础(王卓)(21):哈夫曼编码(1):过程_第2张图片

 在前面定义的过程中:

    char* cd = new char[n];

我们可以看到:

cd是一个指向一维数组char[n]的一个指针

也可以是一个二维数组,由若干个一维数组char【n】作为基本单位所构成

而我们要复制拷贝的,是其中某一列(第 i 列)

所以我们写的语句,不应该是这样的吗?:

        strcpy(HC[i], &cd[i]);  //将求得的编码复制

为什么标准答案写的是

        strcpy(HC[i], &cd[start]);  //相当于我们函数里写的:
        //strcpy(HC[i], &cd[insert]); 

???


最终结果见下一节

你可能感兴趣的:(数据结构(王卓),算法)