Понимание логики для извлечения частоты из двоичного файла для создания дерева Хаффмана

0

Я должен зацикливать частоту из двоичных файлов.

Что я имею в виду, я буду читать символы, присутствующие в файле, а затем вычислять частоту по количеству повторений символа. Я использую этот код. И он отлично работает:

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 - символы в двоичном файле. При первом выполнении addymbol мы делаем addymbol (1); , Теперь мы сохраняем "1", чтобы узнать, появится ли какое-либо другое "1" в будущем или нет? поэтому мы делаем символ pt->, если снова равен "1", тогда мы увеличиваем частоту на единицу. Но при втором выполнении addymbol мы делаем addymbol (2); который не равен "1", поэтому снова повторяйте.

В третий раз я получил addymbol (1); , на этот раз я получил "1", который равен ранее сохраненному "1", поэтому увеличивает частоту на "1". Как насчет предыдущего "2"? Поскольку мы читаем файл только один раз, делая

while (fread(&ch,sizeof(ch),1,fp))
    {
     add_symbol(ch);    
    }

и если "2" уже пройден, мы не сможем его подсчитать. Как этот код сохраняет этот "2", а также находит его частоту. Пожалуйста, не стесняйтесь спрашивать меня, не по-прежнему ли я оставил мой вопрос?

  • 0
    Это на самом деле ваш код или вы просто скопировали его откуда-то? Вы пытаетесь понять, как работает реализация дерева кодирования Хаффмана, или просто понять его реальную реализацию?
  • 0
    Есть очень мало заданий и сравнений, которые являются кандидатами на вопросы 1 и 2. Это не должно быть так сложно решить. (Кстати: вы, кажется, подразумеваете, что написали этот код, но все же спрашиваете, как он работает?)
Показать ещё 4 комментария
Теги:
algorithm
binaryfiles
huffman-code

2 ответа

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 целых чисел).

  • 0
    Спасибо за помощь, предположим, 12133142342 символов хранятся в файле. Впервые передается «1», добавляется символ (1), и он сохраняется в символе ptr->. в следующий раз addymbol (2); но «2» не равно «1», так что не принимайте это во внимание, но в следующий раз добавляется символ (1); , Теперь он нашел «1», поэтому увеличить частоту на единицу. (Question1:) Но как насчет предыдущей «2», которую мы получили при втором выполнении addasymbol (); который не был сохранен, потому что его частота не была равна "1"? (вопрос: 2) Нужно ли нам снова проверять все «2» (и каждый символ) все символы, чтобы сохранить его частоту
  • 0
    Ваше предложение «но« 2 »не равно« 1 », поэтому не принимайте его во внимание» полностью неверно. Истина в том, что «но« 2 »не равно« 1 », поэтому продолжайте цикл по связанному списку, пока не найдете« 2 »; и если не найден, добавьте его с частотой 1. »
Показать ещё 5 комментариев
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.

  • 0
    Спасибо за помощь, но я обязан не использовать какие-либо статические объявления (без массивов) только указатели. Я уже сделал то, что вы сказали, используя массив. Это мой следующий шаг, чтобы правильно понять концепции указателей.
  • 0
    Пожалуйста :)
Показать ещё 12 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню