TableSize
,習慣上使表從0
到TableSize - 1
變化,每一個關鍵字被映射到0
到TableSize - 1
範圍中的某個數並被放在合適的單元中,這個映射就叫散列函數typedef unsigned int Index; Index Hash(const char *Key, int TableSize) { unsigned int HashVal = 0; while (*key != '\0') HashVal += *Key++; return HashVal % TableSize; }
Index Hash(const char *Key, int TableSize) { return (Key[0] + 27 * Key[1] + 729 * Key[2]) % TableSize; }
Index Hash(const char *Key, int TableSize) { unsigned int HashVal = 0; while (*Key != '\0') HashVal = (HashVal << 5) + *Key++; return HashVal % TableSize; }
它的作法就是將散列到同一個值的全部元素保留到一個如圖所示的鏈表數組中數組
//分離連接散列表類型聲明 struct ListNode; typedef struct ListNode *Position; struct Hashbl; typedef struct Hashbl *HashTable; struct ListNode { ElementType Element; Position Next; }; typedef Position List; struct Hashbl { int TableSize; List* TheLists; }; //初始化 HashTable InitializeTable(int TableSize) { HashTable H; int i; if (TableSize < MiniTableSize) { Error("Table size too small"); return NULL; } H = malloc(sizeof(struct HashTbl)); if (H == NULL) FatalError("out of space"); H->TableSize = NextPrime(TableSize); H->TheLists = malloc(sizeof(List) * H->TableSize); if (H->TheLists == NULL) FatalError("out of space"); for (i = 0; i < H->TableSize; i++) { H->TheLists[i] = malloc(sizeof(struct ListNode)); if (H->TheLists[i] == NULL) FatalError("out of space"); else H->TheLists[i]->Next = NULL; } return H; } //尋找一個值 Position Find(Element Key, HashTable H) { Position P; List L; L = H->TheLists[Hash(Key, H->TableSize)]; P = L->Next; while (P != NULL && P->Element != Key) P = P->Next; return P; } //插入一個值 void Insert(ElementType Key, HashTable H) { Position Pos, NewCell; List L; Pos = Find(Key, H); if (Pos == NULL) { NewCell = malloc(sizeof(struct ListNode)); if (NewCell == NULL) FatalError("out of space"); else { L = H->TheLists[Hash(Key, H->TableSize)]; NewCell->Next = L->Next; NewCell->Element = Key; L->Next = NewCell; } } }
如上面的代碼所示,它將新元素插入到表的最前面,不只是由於方便,更是由於新插入的元素有可能最早被訪問,這裏使用了鏈表,天然還能夠想到用二叉查找樹,甚至另外一個散列表來實現,這裏使用了表頭,在空間不足時能夠考慮不使用表頭數據結構
定義散列表的裝填因子\(\lambda\)爲散列表中的元素個數與散列表大小的比值函數
開放定址散列法是另外一種不用鏈表解決衝突的辦法,在開放定址法中,若是有衝突發生,就選擇嘗試另外的單元,直到找到空的單元爲止,單元\(h_0(X)\),\(h_1(X)\),\(h_2(X)\)等相繼被試選,其中\(h_i(X) = (Hash(X) + F(i))\) \(mod\) \(TableSize\),\(F(0) = 0\),函數\(F\)爲衝突解決方法spa
即\(F(i) = i\)的情形,就是逐個探測每一個單元格以查找出一個空單元,這種方法的一個問題就是即便表格相對較空,佔據的單元也會造成一些區塊,其結果稱爲一次聚焦code
即\(F(i) = i^2\)的情形,它消除了線性探測的一次聚焦問題,關於它有一個定理:若是使用平方探測,且表的發小是素數(兩個條件都要知足),那麼當表至少有一半是空的時候,總可以插入一個新的值。雖然平方探測消除了一次聚焦,可是散列到同一位置的元素將探測相同的備選單元,稱爲二次聚焦,模擬結果指出,它通常要引發另外的少於一半的探測blog