最近在学C语言··········
然后去看了浙大的数据结构,在里面看到了········一道哈夫曼编码的算法题:
题意如下:
给定一段文字,统计出字母出现的频率,是可以根据哈夫曼算法给出一套编码,使得用此编码压缩原文可以得到最短的编码总长。然而哈夫曼编码并不是唯一的
-------就是说用0和1来表示不同频率的字母,使用不等长码,让最后得到的编码占用内存最小--------
例如对字符串"aaaxuaxz",容易得到字母 ‘a’、‘x’、‘u’、‘z’ 的出现频率对应为 4、2、1、1。我们可以设计编码 {‘a’=0, ‘x’=10, ‘u’=110, ‘z’=111},也可以用另一套 {‘a’=1, ‘x’=01, ‘u’=001, ‘z’=000},还可以用 {‘a’=0, ‘x’=11, ‘u’=100, ‘z’=101},三套编码都可以把原文压缩到 14 个字节。
但是 {‘a’=0, ‘x’=01, ‘u’=011, ‘z’=001} 不是哈夫曼编码,因为这套编码压缩得到 00001011001001 后,解码的结果不唯一,“aaaxuaxz” 和 “aazuaxax” 都可以对应解码的结果
本题就请你判断任一套编码是否哈夫曼编码
输入格式如下:
给出 一个字符+空格+该字符出现频率+空格+······
且字符数量少于64个,频率不大于1000
给出 一个整数 (表示上传数据的学生总数)
给出 多行数据(行数等于字符个数) 每一行对应 一个字符 + 学生给出的编码
如下:
输入示例:
7
A 1 B 1 C 1 D 3 E 3 F 6 G 6
4
A 00000
B 00001
C 0001
D 001
E 01
F 10
G 11
A 01010
B 01011
C 0100
D 011
E 10
F 11
G 00
A 000
B 001
C 010
D 011
E 100
F 101
G 110
A 00000
B 00001
C 0001
D 001
E 00
F 10
G 11
输出示例:
Yes
Yes
No
No
然后我是看完了姥姥的课后才去做的··········
但是我写的代码就是怎么也跑不起来😟
我的思路如下:
首先将字符和频率数据都储存在链表中
然后单独开一个数组储存频率以便之后计算学生上传的编码长度使用
再将字符和编码储存在链表中(另一种类型的链节)
然后进行以下的判断:
1.判断是否为最少编码量
实现步骤:
将链表建成树,使用频率最低的两个字符所在的链节会被新建的链节取代,将其中的字符设置为\0,使用频率设置为两个使用频率之和,最后把这两个链节分别分配给新建链节的左右指针,建成树之后可以计算出最少编码量BestCode
2.判断是否无歧义
实现步骤:
按姥姥的说法,只要能够建成一棵树,使所有字符都在叶节点上就可以保证不出现歧义
因此进行建树,建树的过程中遇到不符合的就返回0
然后我写出的代码如下:
当然这个代码是错的···········但是我就是找不出来错在哪里了
debug了好久,还是不知道为什么每次的结果都不一样··········
而且最绝的是总是在途中使用了无访问权限的指针( 野指针?)
报错也爆的不明不白············
请求大佬指点············
我真的累了 写了一整天吧·············从能跑得动到跑不动·········
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct HuffmanNode Node;
typedef Node* PNode;
struct HuffmanNode
{
char character;
int Data;
PNode Next;
PNode left;
PNode right;
};
typedef struct StudentNode StuNode;
typedef StuNode* PStuNode;
struct StudentNode
{
char character;
char codearr[7];
PStuNode StuNext;
PStuNode StuLeft;
PStuNode StuRight;
};
int arr[64] = { 0 }; //储存频率
int num, StudentN, BestCode; //定义储存的字符个数和提交编码的学生数量
PNode CreateHuffmanTree(PNode tmp)//--------------------建树函数-----------------------
{
PNode ptr = NULL;
if (tmp->Next != NULL)
{
while (tmp->Next->Next != NULL)//至少有两个链节
{
if (tmp->Next->Next->Next != NULL)//头结点后至少有3个链结
{
if (tmp->Next->Data <= tmp->Next->Next->Next->Data)
{//A结点<=C节点的值,AB结点结合(第1、2个链结结合)
ptr = (PNode)malloc(sizeof(Node));
ptr->Next = tmp->Next->Next->Next;
ptr->character = 0;
ptr->Data = tmp->Next->Data + tmp->Next->Next->Data;
//建树
ptr->left = tmp->Next;
ptr->right = tmp->Next->Next;
tmp->Next = ptr;
}
else
{//A结点>C节点的值,BC结点结合(第2、3个链结结合)
ptr = (PNode)malloc(sizeof(Node));
ptr->character = 0;
ptr->Data = tmp->Next->Next->Next->Data + tmp->Next->Next->Data;
ptr->left = tmp->Next->Next;
ptr->right = tmp->Next->Next->Next;
ptr->Next = tmp->Next->Next->Next->Next;
tmp->Next->Next = ptr;
}
}
else
{
ptr = (PNode)malloc(sizeof(Node));
ptr->character = 0;
ptr->Data = tmp->Next->Data + tmp->Next->Next->Data;
//建树--ptr即为根节点的指针
ptr->left = tmp->Next;
ptr->right = tmp->Next->Next;
tmp->Next = ptr;
ptr->Next = NULL;
}
}
return tmp->Next;
}
return NULL;
}
int CaculateBestCode(PNode ptr, int count) //---------计算最小编码量----------
{
int sum = 0;
if (ptr)
{
if (ptr->character != 0)//判断是否有内容
{
sum += ptr->Data * count;
}
else//递归进入左右子树
{
count++;
sum += CaculateBestCode(ptr->left, count);
sum += CaculateBestCode(ptr->right, count);
}
return sum;
}
return 0;
}
int IsTheBest(PNode First, PStuNode StuFirst) //----------判断是否为最少编码量----------------
{
int sum = 0, length;
PStuNode stu_tmp = StuFirst->StuNext;
for(int i=0;i<num;i++)
{
//编码数量等于每一个字符编码长度(一定小于7)*使用频率
if ((length = strlen(stu_tmp->codearr)) < 7)
{
sum += length * arr[i];
stu_tmp = stu_tmp->StuNext;
}
else { return 0; }
}
if (sum == BestCode) { return 1; }
return 0;
}
int IsCorrect(PStuNode StuFirst)
{
//进行建树,如果建树成功则说明编码可行
PStuNode ptr = (PStuNode)malloc(sizeof(StuNode));
PStuNode tmp = StuFirst->StuNext;//tmp指向链表的第一个非空链结
ptr->character = 0;
ptr->StuLeft = NULL;
ptr->StuRight = NULL;
PStuNode ptr_tmp = ptr;//保存下根节点的位置
while (tmp)
{
ptr=ptr_tmp;
for (int i = 0; i < 7; i++)
{
if (tmp->codearr[i] == '1')
{
if (ptr->StuRight == NULL)
{
ptr->StuRight = (PStuNode)malloc(sizeof(PStuNode));
ptr = ptr->StuRight;
ptr->character = 0;
ptr->StuLeft = NULL;
ptr->StuRight = NULL;
}
else
{
ptr = ptr->StuRight;
if (ptr->character != 0) { return 0; }
}
}
else if (tmp->codearr[i] == '0')
{
if (ptr->StuLeft == NULL)
{
ptr->StuLeft = (PStuNode)malloc(sizeof(PStuNode));
ptr = ptr->StuLeft;
ptr->character = 0;
ptr->StuLeft = NULL;
ptr->StuRight = NULL;
}
else
{
ptr = ptr->StuLeft;
if (ptr->character != 0) { return 0; }
}
}
else
{//遇到\0,为停止处,检查是否有子节点
//检查该编码是否未被使用
if (ptr->character == 0) { ptr->character = tmp->character; }
else { return 0; }
//该编码无歧义
if (ptr->StuLeft == NULL && ptr->StuRight == NULL) { break; }
else { return 0; }
}
}
//检查下一编码
tmp = tmp->StuNext;
}
return 1;
}
int main(void) //------------主函数main开始------------
{
//完成频率和字符的存储
scanf("%d", &num);
PNode First = (PNode)malloc(sizeof(Node));
PNode tmp = First;
for (int i = 0; i < num; i++)//将数据储存在链表中
{
tmp->Next = (PNode)malloc(sizeof(Node));
scanf(" %c %d ", &tmp->Next->character, &tmp->Next->Data);
arr[i] = tmp->Next->Data;
tmp = tmp->Next;
tmp->Next=NULL;
}
BestCode = CaculateBestCode(CreateHuffmanTree(First), 0);
//完成学生数据的读取存入
scanf(" %d ", &StudentN);
PStuNode StuFirst = (PStuNode)malloc(sizeof(Node));
for (int j = 0; j < StudentN; j++)
{
PStuNode stu_tmp = StuFirst;
for (int i = 0; i < num; i++)//将数据储存在链表中
{
getchar();
stu_tmp->StuNext = (PStuNode)malloc(sizeof(Node));
scanf("%c %s", &stu_tmp->StuNext->character, stu_tmp->StuNext->codearr);
stu_tmp = stu_tmp->StuNext;
stu_tmp->StuNext = NULL;
}
//判断是否产生最少编码量
if (IsTheBest(First, StuFirst))
{
//判断是否产生歧义
if (IsCorrect(StuFirst)) { printf("Yes\n"); }
else { printf("No\n"); }
}
else { printf("No\n"); }
}
return 0;
}