逻辑雏形
根据老师讲解的思路,梳理出程序运行的逻辑雏形如下:
- 搞一个多维数组HC,用来存储我们这里 n(每) 个节点的哈夫曼编码
- 搞一个数组cd,用来存储我们这里每个节点是前面一位的左子树(0)还是右子树(1),给cd最后一位放结束存储字符
- 反复向上回溯:每次都通过找(parent)来判断自己是左子树还是右子树,并且在cd表里记录每次的查询结果,然后再继续向上回溯看上一层(parent),在进行同样(相同)的循环操作
- 给HC开辟相应的,每个节点对应需要的存储空间大小的空间
- 每算完一个节点的哈夫曼编码就把它放(复制)到HC里面
- 释放空间,结束
需要注意的是,由于我们的函数抬头是:
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';
可是我觉得不对吧,为什么在数组倒数第二个位置上放空字符啊?
根据前面实际(操作)的案例,空字符不应该是放在最后一位吗??
🤷
第三步:
生成哈夫曼编码
我写的:(第一版)
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]); //将求得的编码复制
}
两种做法思路不同:
我的思路:
- 每次都从cd表最后一位往前逐个查找,直至找到第一个空着的位置
- 往里面输入数据
- 输入完以后直接跳出查找,直接进入下一轮for(i)的循环
标准答案:
- 先设立一个变量s,表示(记录)每次cd表当中输入的分支(边)其对应的位置序号
- 每输入cd表1次,s就向前指一个位置
- 输入
- 把比较使用的结点改成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;
}
第四步:(这里我们就把最后收尾工作合并一起写了)
- 给HC开辟相应的,每个节点对应需要的存储空间大小的空间
- 每算完一个节点的哈夫曼编码就把它放(复制)到HC里面
- 释放空间,结束
HC[i] = new char[n - insert];
//copy H[i] from cd to HC;
这里,我们可以按标准答案:
使用 strcpy函数(虽然实际上我也没听说过),最终再用delete释放空间
当然这个过程中还是碰到了一些
问题:
在前面定义的过程中:
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]);
???
最终结果见下一节