統計檔案裡出現次數前10的單詞
阿新 • • 發佈:2021-02-10
技術標籤:# 演算法題
統計” The_Holy_Bible_Res.txt “ 中字元的個數,行數,單詞的個數,統計單詞的詞頻並列印輸出詞頻最高的前 10 個單詞及其詞頻
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXKEY 10000 #define SWAP(a,b) {pInfo_t t=a;a=b;b=t;} int hash(char *key) {//雜湊函式:輸入字串的地址返回字串對應的雜湊值 int h = 0 , g; while (*key) { h = (h << 4) + *key++; g = h & 0xf0000000; if (g) h ^= g >> 24; h &= ~g; } return h % MAXKEY; } typedef struct info { int num;//單詞詞頻 char *address;//單詞地址 struct info *next;//指向雜湊衝突連結串列的下一個結點 }Info_t , *pInfo_t; int isLetterofAlphabet(char c)//判斷小寫字母 { if (c <= 122 && c >= 97) { return 1; } else { return 0; } } //陣列下標從0開始 //left是堆頂的下標,編號是left+1 //左孩子編號2*left+2,下標是2*left+1 //右孩子編號2*left+3,下標是2*left+2 void adjustMinHeap(pInfo_t *p , int left , int right)//調整為小頂堆 {//自上而下調整 int father = left; int son = 2 * father + 1;//son指向更小的孩子 while (son <= right) { if (son + 1 <= right && (p[son]->num > p[son + 1]->num))//son+1<=right保證了沒有右孩子就不會比較兩個孩子 { son = son + 1;//右孩子小就指向右孩子 } if (p[father]->num > p[son]->num) { SWAP(p[father] , p[son]); father = son; son = 2 * father + 1; } else { break; } } } void buildMinHeap(pInfo_t *p , int left , int right)//建立小頂堆 { int lastFather = (right - 1) / 2;//最後一個分支結點 for (int i = lastFather; i >= left; i--)//自下而上建堆 { adjustMinHeap(p , i , right); } } /* 沒有用到這個函式,只是提供了一種找出檔案裡的單詞的思路 缺點:如果指標所指向的是檔案裡面第一個字元,那麼p[-1]就錯了 */ int isWord(char *p) {//檔案指標此時指向的是字母,如果前面不是字母,說明這是一個單詞 if (p[-1] < 97 || p[-1]>122) { return 1; } else { return 0; } } //2 int main(int argc , char *argv[]) { FILE *fp = fopen(argv[2] , "rb"); if (fp == NULL) { perror("fp.fopen:"); return -1; } pInfo_t hashTable[MAXKEY] = { NULL }; char c; int n1=0 , n2=1 , n3=0;//字元的個數,行數,單詞的個數 while ((c = fgetc(fp)) != EOF) { /* 錯誤:在檔案裡單詞的結尾不是'\0',這樣把單詞地址輸入hash函式得到的雜湊值錯 if (c <= 122 && c >= 97)//小寫字母 { if (isWord(&c))//是單詞 { n3++; pInfo_t p = (pInfo_t) calloc(1 , sizeof(Info_t)); p->num++; p->address = &c; //錯:每遇到一個單詞就創新結點p,用p->num==1去判斷單詞以前是否出現過是不合理的 if (hashTable[hash(&c)] != NULL&&p->num==1) {//這個結點第一次出現並且發生了衝突 pInfo_t q = hashTable[hash(&c)]; while (q->next!=NULL) { q = q->next; } q->next = p; } */ if (isLetterofAlphabet(c))//小寫字母 { n3++; n1++; int i = 0; //錯誤的寫法:char word[100] = {0}; char *word = (char*) calloc(1 , 100); word[i++] = c; while ((c = fgetc(fp)) != EOF && isLetterofAlphabet(c)) { word[i++] = c; n1++; } if (hashTable[hash(word)] != NULL) {//單詞發生了衝突或者是單詞之前出現過 pInfo_t q = hashTable[hash(word)]; //比較雜湊元素對應的單詞是否和word相同 while (q->next != NULL && strcmp(q->address , word)!=0) { q = q->next; } if (strcmp(q->address , word) == 0) { q->num++;//衝突連結串列裡有word這個單詞,詞頻+1 } else { pInfo_t p = (pInfo_t) calloc(1 , sizeof(Info_t)); p->address = word; p->num = 1; q->next = p; } } else {//單詞沒發生衝突並且是第一次出現 pInfo_t p = (pInfo_t) calloc(1 , sizeof(Info_t)); p->address = word; p->num = 1; hashTable[hash(word)] = p; } if (c == EOF)//到達檔案尾 { break; } } if(c=='\n')//遇到'\n' { n2++; } } fclose(fp); printf("字元數%d,行數%d,單詞數%d\n" , n1 , n2 , n3); pInfo_t A[10] = {NULL};//存放前10大詞頻的結點指標 int flag = 0;//標識是否建立了初始的前10個結點指標的小頂堆 //掃描雜湊連結串列 for (int i = 0,j=0; i < MAXKEY; i++) { if (hashTable[i]!=NULL) { pInfo_t t= hashTable[i]; while (t!=NULL) { if (j<10)//把前10個結點指標存進A { A[j++] = t; } else if(j==10&&flag==0) { buildMinHeap(A , 0 , 9); //建立小頂堆 flag = 1; } else { if (A[0]->num < t->num) { //錯:SWAP(A[0],t); 影響t = t->next; A[0] = t; adjustMinHeap(A , 0 , 9); } } t = t->next; } } } for (int i = 0; i < 10; i++) {//列印輸出詞頻最高的前 10 個單詞及其詞頻 printf("%s:%d\n" , A[i]->address , A[i]->num); } return 0; }