我必须从二进制文件中剔除频率。
我想到的是我会读取文件中的字符,然后按字符重复的次数计算频率。 我这样做是使用此代码。它工作正常:
struct Node
{
unsigned char symbol;
int appear;
struct Node *link;
struct Node * left,*right;
};Node * head;
在主要的某个地方,我喜欢这样阅读文件:
ch = fgetc(fp);
while (fread(&ch,sizeof(ch),1,fp))
{
symbol(ch);
}
fclose(fp);
其中add_symbol函数是这样的:
但是我无法理解这段代码的逻辑。可以请任何人解释我在代码中提出的问题吗?
symbol(unsigned char sym)
{
Node*pt,*pt,*t;
int is_there=0;
pt = pt = head;
while (pt != NULL)
{
if (pt -> symbol == sym)
{
pt -> appear++;
is_there = 1;
break;
}
pt = pt;
pt = pt -> link;
}
if (!is_there)
{
// printf("\n is_there2 : %d\n",!is_there);
printf("sym2 : %d\n", sym);
t = (Node *) malloc(sizeof( Node));
t -> symbol = sym;
t -> appear = 1;
t -> left = NULL;
t -> right = NULL;
t->link = NULL;
if (head == NULL)
{
head = temp;
}
else
{
pt->link = temp;
}
}
}
要找到相同的频率,我们需要先将所有数据存储在某处。
(1)哪里完成了?
(2)如果再次出现,我们需要比较符号吗?
(3)请详细解释c和c ++中逻辑相同的代码。所以任何语言,没有问题。
在解释中我怀疑: 假设1 2 1 3 3 1 2是二进制文件中的符号。 在第一次执行addsymbol时,我们执行addsymbol(1); ,现在我们存储“1”以了解未来是否还有其他“1”? 所以我们做pt->符号如果再次等于“1”那么我们将频率增加1。 但是在第二次执行addsymbol时,我们会添加addsymbol(2);这不等于“1”,所以再次重复。
第三次执行时我得到了addsymbol(1); ,这次我得到的“1”等于之前存储的“1”,所以频率增加“1”。 那之前的“2”怎么样?因为我们只通过
读取文件一次while (fread(&ch,sizeof(ch),1,fp))
{
add_symbol(ch);
}
如果“2”已经通过,那么我们将无法计算它。这段代码如何保持这个“2”并且还发现它的频率请不要犹豫,问我是否还没有解决我的问题?
答案 0 :(得分:3)
代码不存储所有数据,它只将符号和计数存储在链表中。
代码一次读取一个符号,为每个符号调用add_symbol()。 add_symbol
函数首先在链接列表中查找符号。如果符号在那里,函数将只增加其计数;否则,它会将符号添加到列表的尾部,并且计数为1.
编辑:根据请求,如果分解得更多,它的外观如下:
void Huffman::add_symbol(unsigned char sym)
{
Node * foundNode = find_node_in_linked_list(sym);
if(foundNode != NULL)
foundNode->freq++;
else
add_freq1_node_at_end_of_list(sym);
}
Node* Huffman::find_node_in_linked_list(unsigned char sym)
{
Node* pCur = Start;
while(pCur != NULL)
{
if(pCur->symbol == ch)
return pCur;
pCur = pCur->next;
}
return NULL;
}
void Huffman::add_freq1_node_at_end_of_list(unsigned char sym)
{
//Get tail of list
Node* pTail = NULL;
Node* pCur = Start;
while(pCur != NULL)
{
pTail = pCur;
pCur = pCur->next;
}
//Now, pTail is either the last element, or NULL if the list is empty.
//Create the new object
//(should use the new keyword instead, but since the deletion code was not posted...
Node* pNew = static_cast< Node* >(malloc(sizeof *pNew));
if(pNew == NULL)
return;
pNew->symbol = sym;
pNew->freq = 1;
pNew->left = NULL;
pNew->right = NULL;
pNew->next = NULL;
pNew->is_processed = 0;
//Add the new node at the tail
if(pTail != NULL)
pTail->next = pNew;
else
Start = pNew;
}
请注意,它比大函数效率低,因为当找不到符号时它会通过列表两次(一次尝试找到符号,一次找到尾部)。
事实上,没有理由专门添加尾部而不是插入头部。
坦率地说,链表不是存储最多256个符号的计数的最省时的方法。我个人建议使用查找表(256个结构的哑向量,甚至是一个专用的直方图对象,它只是256个整数的向量)。
答案 1 :(得分:2)
有关您的总体设计的一些建议:
步骤1:为了计算符号,您可以使用简单的直方图:
include <limits.h>
int histogram[1<<CHAR_BIT] = {0};
unsigned char ch;
while (fread(&ch,sizeof(ch),1,fp))
histogram[ch]++;
步骤2:现在你需要使用直方图来构建一个霍夫曼树:
Node
指针数组,每个指针对histogram
中的每个条目都有一个,其值大于0。Node
元素。Node
,其子级是这两个Node
元素。Node
插回堆中。步骤3:现在你有一张霍夫曼树,请注意以下几点:
Node
指针数组中给出)。您可以在以下位置查看完整示例:
http://planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=9737&lngWId=3