0

ハッシュテーブルを使って頻度単語数を数えるプログラムを書いていますが、それを並べ替える方法がありません。

構造体を使用して値とカウントを格納します。

私のハッシュコード生成関数はモジュールを使用しており、ハッシュテーブルはリンクリストで使用しています。

1.私の質問は、頻度で並べ替える方法です。

2.なぜ印刷された実行時間が常にゼロなのか疑問に思いますが、何度もチェックしています。間違った方法はどこにありますか?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>

#define  HASHSIZE 29989 
#define  FACTOR 31
#define  VOCABULARYSIZE 30
typedef struct HashNode HashNode;
struct HashNode{
    char* voc;//vocabulary
    int freq;//frequency
    struct HashNode *next;//pointed to the same hashcode 
                          //but actually are different numbers
};

HashNode *HashTable[HASHSIZE] = {NULL,0,NULL};//an array of pointers

unsigned int HashCode(const char *pVoc){//generate hashcode
    unsigned int index = 0;
    int n = strlen(pVoc);
    int i = 0;
    for(; i < n; i ++)
        index = FACTOR*index + pVoc[i];
    return index % HASHSIZE;
}

void InsertVocabulary(const char *pVoc){//insert vocabulary to hash table
    HashNode *ptr;
    unsigned int index = HashCode(pVoc);
    for(ptr = HashTable[index]; ptr != NULL; ptr = ptr -> next){//search if already exist
        if(!strcmp (pVoc, ptr -> voc)){
            (ptr->freq)++;
            return;          
        }        
    }
    ptr = (HashNode*)malloc(sizeof(HashNode));//if doesn't exist, create it
    ptr -> freq = 1; 
    ptr -> voc = (char*)malloc(strlen(pVoc)+1);
    strcpy(ptr -> voc, pVoc);
    ptr -> next = HashTable[index];
    HashTable[index] = ptr;
}

void ReadVocabularyTOHashTable(const char *path){
    FILE *pFile;
    char buffer[VOCABULARYSIZE];
    pFile = fopen(path, "r");//open file for read
    if(pFile == NULL)
        perror("Fail to Read!\n");//error message
    char ch;
    int i =0;
    do{
        ch = fgetc(pFile);
        if(isalpha(ch))
            buffer[i++] = tolower(ch);//all convert to lowercase                
        else{
            buffer[i] = '\0';//c-style string
            i = 0;
            if(!isalpha(buffer[0]))
                continue;//blank line
            else //printf("%s\n",buffer);
                InsertVocabulary(buffer);
        }
    }while(ch != EOF);
    fclose(pFile);
}

void WriteVocabularyTOHashTable(const char *path){
    FILE *pFile;
    pFile = fopen(path, "w");
    if(pFile == NULL)
        perror("Fail to Write\n");
    int i = 0;
    for(; i < HASHSIZE; i++){
        HashNode *ptr = HashTable[i];
        for(; ptr != NULL; ptr = ptr -> next){
            fprintf(pFile, "Vocabulary:%s,Count:%d\n", ptr -> voc, ptr -> freq);
            if(ptr -> next == NULL)
                fprintf(pFile,"\n");
        }
    }
    fclose(pFile);
}

int main(void){
    time_t start, end;
    time(&start);
    ReadVocabularyTOHashTable("test.txt");
    WriteVocabularyTOHashTable("result.txt");
    time(&end);
    double diff = difftime(end,start);
    printf("%.21f seconds.\n", diff); 
    system("pause");
    return 0;     
}
4

1 に答える 1

2

これは、頻度で並べ替えた最初の質問に対する回答です。テーブル内のすべてのハッシュノードは、個別の語彙エントリです。同じコードへのハッシュ(したがってコリジョンチェーン)もありますが、最終的には、一意のエントリごとに1つのHashNodeがあります。既存のコードの邪魔を最小限に抑えて頻度で並べ替えるには、qsort()をポインターリスト(または他の任意の種類)で比較的簡単に使用できます。

注:これを行う最も効率的な方法は、語彙挿入中にソートされたリンクリストを維持することであり、それを検討することをお勧めします。このコードは、ハッシュテーブルが既に入力されており、頻度を最高から最低の順に並べ替えて取得する必要があることを前提としています。

まず、すべての一意の挿入の実行中の集計を維持します。非常に簡単です。割り当てサブセクションにカウンターを追加するだけです。

gVocabCount++; // increment with each unique entry.
ptr = (HashNode*)malloc(sizeof(HashNode));//if doesn't exist, create it
ptr -> freq = 1; 
ptr -> voc = (char*)malloc(strlen(pVoc)+1);
strcpy(ptr -> voc, pVoc);
ptr -> next = HashTable[index];
HashTable[index] = ptr;

次に、一意の語彙の総数と同じ大きさのポインタのリストをHashNodeに割り当てます。次に、コリジョンチェーンを含むハッシュテーブル全体をウォークし、各ノードをこのリストのスロットに配置します。リストは、ノードの総数と同じサイズにするか、何か間違ったことをしました

HashNode **nodeList = malloc(gVocabCount * sizeof(HashNode*));

int i;
int idx = 0;
for (i=0;i<HASHSIZE;++i)
{
   HashNode* p = HashTable[i];
   while (p)
   {
       nodeList[idx++] = p;
       p = p->next;
   }
}

これで、すべての一意のノードポインタのリストができました。qsort()に送信する比較関数が必要です。番号が最も大きいアイテムをリストの先頭に配置する必要があります。

int compare_nodeptr(void* left, void* right)
{
    return (*(HashNode**)right)->freq - (*(HashNode**)left)->freq;
}

そして最後に、qsort()を起動してポインタリストを並べ替えます。

qsort(nodeList, gVocabCount, sizeof(HashNode*), compare_nodeptr);

HashNodeポインターのnodeList配列では、すべてのノードが降順で並べ替えられます。

for (i=0; i<gVocabCount; ++i)
   printf("Vocabulary:%s,Count:%d\n", nodeList[i]->voc, nodeList[i]->freq);

最後に、リストを解放することを忘れないでください。

free(nodeList);

最初に述べたように、これを行う最も効率的な方法は、増分値をプルし(定義上、すべての新しいエントリは最後に移動できます)、挿入ソートを実行してそれをに戻すソートされたリンクリストを使用することです。適切な場所。最終的に、そのリストは上記のコードが作成するものと実質的に同じになります(like-count-orderは耐えられません。つまり、a-> freq=5およびb->freq= 5、abまたはbaのいずれかが発生する可能性があります)。

お役に立てれば。

編集:ソートされたデータを出力する書き込み関数がどのように見えるかをOPに示すように更新されました:

static int compare_nodeptr(const void* left, const void* right)
{
    return (*(const HashNode**)right)->freq - (*(const HashNode**)left)->freq;
}

void WriteVocabularyTOHashTable(const char *path)
{
    HashNode **nodeList = NULL;
    size_t i=0;
    size_t idx = 0;

    FILE* pFile = fopen(path, "w");
    if(pFile == NULL)
    {
        perror("Fail to Write\n");
        return;
    }

    nodeList = malloc(gVocabCount * sizeof(HashNode*));
    for (i=0,idx=0;i<HASHSIZE;++i)
    {
        HashNode* p = HashTable[i];
        while (p)
        {
            nodeList[idx++] = p;
            p = p->next;
        }
    }

    // send to qsort()
    qsort(nodeList, idx, sizeof(HashNode*), compare_nodeptr);

    for(i=0; i < idx; i++)
        fprintf(pFile, "Vocabulary:%s,Count:%d\n", nodeList[i]->voc, nodeList[i]->freq);

    fflush(pFile);
    fclose(pFile);
    free(nodeList);
}

とにかく、そのようなもの。OPのテストファイルから、出力の上位数行は次のとおりです。

Vocabulary:the, Count:912
Vocabulary:of, Count:414
Vocabulary:to, Count:396
Vocabulary:a, Count:388
Vocabulary:that, Count:260
Vocabulary:in, Count:258
Vocabulary:and, Count:221
Vocabulary:is, Count:220
Vocabulary:it, Count:215
Vocabulary:unix, Count:176
Vocabulary:for, Count:142
Vocabulary:as, Count:121
Vocabulary:on, Count:111
Vocabulary:you, Count:107
Vocabulary:user, Count:102
Vocabulary:s, Count:102
于 2012-10-20T18:43:56.523 に答える