1. 程式人生 > >基於字串的分離連結hash演算法

基於字串的分離連結hash演算法

Hashes
問題:你有一個很大的字串陣列。需要知道另外一個字串是否在這個字串陣列中。你可能會將這個字串與陣列中的字串依次作比較。但是實際中,你會發現這種方法太慢。必須找其它的方法。但是除了依次比較字串外,還有沒有其它方法來知道某個字串是否存在呢?
解決方案: Hashes。 Hashes是用小的資料型別(如,數字)來表示其它大的資料型別(通常是字串)。在這種情形下,你可能將字串儲存在hash陣列中。然後你可以計算要查詢字串的hash值,用這個hash值與陣列中的hash值進行比較。如果在hash陣列中有一個hash值與這個新的要查詢的hash值相等,則證實這個字串存在。這個方法, 稱為索引(indexing)。
本文采用分離連結hash演算法來實現基於字串的hash演算法,並且可以統計某個字串出現的次數

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

/*定義hash節點*/
struct hash_node {
    char *value;   /*字串資料,動態分配記憶體*/
    int count;      /*此字串出現的次數*/
    struct hash_node * next; /*解決衝突的分離連結法的next節點*/
};
/*定義hash表結構
 *  *兩種方式:
 *   * 1. 用陣列定義
 *    * 2. 用連結串列*/
/*用陣列*/
#define MAX_HASH_TABLE 10000
/*用連結串列表示*/ struct hash_table { int num; /*記錄hash表的大小*/ struct hash_node **hashlist; /*用指標動態分配hash陣列記憶體大小*/ }; typedef struct hash_node * hash_list; typedef struct hash_table* Hash_Table; /*根據hash表大小,初始化hash表*/ Hash_Table init_hash_table(int hash_size) { Hash_Table hashtable; int i; hashtable = (Hash_Table)malloc(sizeof(struct hash
_table)); if(hashtable == NULL) { printf("malloc hashtable error\n"); return NULL; } hashtable->num = hash_size;/*hash陣列大小*/ /*為hash陣列動態分配記憶體*/ hashtable->hashlist = (struct hash_node **)malloc(sizeof(struct hash_node*) * hash_size); if(hashtable->hashlist == NULL) { printf("malloc hashlist error\n"); free(hashtable); hashtable = NULL; return NULL; } /*根據hash陣列的大小,為每一個成員分配記憶體,並且初始化記憶體*/ for(i = 0; i < hash_size; i++) { hashtable->hashlist[i] = (struct hash_node*)malloc(sizeof(struct hash_node)); if(hashtable->hashlist[i] == NULL) { printf("malloc hashtable->hashlist error\n"); exit(1); }else { hashtable->hashlist[i]->value = NULL; hashtable->hashlist[i]->count= 0; hashtable->hashlist[i]->next = NULL; } } return hashtable; } /*獲取hash key值的hash演算法函式*/ unsigned long get_hash_index(const char *key,int hash_size) { unsigned long ulHash = 0; while(*key) { ulHash += (ulHash << 5) + *key++; } return (ulHash % hash_size); } /*在hash表中插入一個字串*/ int hash_insert(char *string, Hash_Table hash_table) { unsigned long index; hash_list hash; index = get_hash_index(string,hash_table->num); hash = hash_table->hashlist[index]; if(hash == NULL) { hash = (hash_list)malloc(sizeof(struct hash_node)); if(hash == NULL) { printf("error: malloc hashlist failed\n"); return -1; }else { memset(hash,0,sizeof(struct hash_node)); hash->value = (char*)malloc(strlen(string)+1); hash->count++; strncpy(hash->value,string,strlen(string)+1); } }else { while(hash) { if(hash->value != NULL) { if(strcmp(hash->value,string) == 0) { hash->count++; return 0; } hash=hash->next; }else { hash->value = (char*)malloc(strlen(string)+1); hash->count++; strncpy(hash->value,string,strlen(string)+1); return 0; } } } return 0; } hash_list hash_find(const char *string, Hash_Table hash_table) { unsigned long index; hash_list hash; index = get_hash_index(string,hash_table->num); hash = hash_table->hashlist[index]; while(hash) { if((hash->value != NULL) && (strcmp(hash->value,string) == 0)) { printf("find %s in hash table.....\n",string); return hash; } hash = hash->next; } return NULL; } int main(int argc, char *argv[]) { Hash_Table hash_table; int rc = 0; hash_list hash; hash_table = init_hash_table(MAX_HASH_TABLE); //rc = hash_insert("wgw",hash_table); rc = hash_insert("cdef",hash_table); rc = hash_insert("abcd",hash_table); rc = hash_insert("cdef",hash_table); hash = hash_find("cdef",hash_table); if(hash) { printf("hit num of cdef is %d\n",hash->count); } hash = hash_find("wgw",hash_table); printf("%s\n",hash?"find wgw":"can't find wgw"); if(hash) printf("num=%d\n",hash->count); }

執行結果:
這裡寫圖片描述

海量資料面試題:
搜尋引擎會通過日誌檔案把使用者每次檢索使用的所有檢索串都記錄下來,每個查詢串的長度為1-255位元組。
假設目前有一千萬個記錄(這些查詢串的重複度比較高,雖然總數是1千萬,但如果除去重複後,不超過3百萬個。一個查詢串的重複度越高,說明查詢它的使用者越多,也就是越熱門。),請你統計最熱門的10個查詢串,要求使用的記憶體不能超過1G。

分析:
不超過3百萬個,假設都是最大的255個位元組,加上next指標和技術count總共255+4+4=263bytes
3000000*263=789000000~~~~789Mbytes小於1G記憶體。而且這個是考慮到極限情況,一般不會所以都是255位元組。
可以考慮用上面的hash演算法,來統計次數,然後用排序演算法獲取最大的10個查詢串。