散列是一種用於以常數平均時間執行插入,刪除和查找的技術
一般想法
一個關鍵字就是一個帶有相關值的字符串。我們把表大小記作Table-Size,並將其理解為散列數據結構的一部分而不僅僅是浮動於全局的某個標量。
每個關鍵字被映射到從0到TableSize-1的這個范圍中的某個數,這個映射就叫做散列函數
散列應用:
- 編譯器中使用散列表跟蹤源代碼。這種數據結構叫做符號表
- 在游戲編制過程中,程序搜索游戲不同行書,它跟蹤通過計算基於位置的散列函數而看到的一些位置。如果同樣的位置再出現,程序通常通過簡單移動變換來避免昂貴的操作。游戲的這種一般特別叫做變換表
- 在線拼寫檢驗程序。將整個字典預先散列,單詞則可以在常數時間內別檢測。
當出現散列值相同的兩個元素時,將產生沖突,下面提供兩種比較簡單的解決方法
分離鏈接法
做法:將散列的同一個值保留到一張表中。如下圖:
1.#include <stdio.h> 2. 3.struct ListNode; 4.typedef struct ListNode *Position; 5.struct HashTbl; 6.typedef struct HashTbl *HashTable; 7. 8. 9.struct ListNode 10.{ 11. int Element; 12. Position Next; 13.}; 14. 15.typedef Position List; 16. 17.struct HashTbl { 18. int TableSize; 19. List *TheLists; 20.}; 21. 22.HashTable InitializeTable(int TableSize) { 23. HashTable H; 24. int i; 25. 26. if (TableSize < 100) 27. { 28. Error("Table size too small"); 29. return NULL; 30. } 31. //分配空間 32. H = malloc(sizeof(struct HashTbl)); 33. if (H == NULL) 34. { 35. FatalError("Out of space"); 36. } 37. //設置表大小為素數 38. H->TableSize = NextPrime(TableSize); 39. //分配數組空間大小 40. H->TheLists = malloc(sizeof(List)*H->TableSize); 41. 42. //分配表頭 43. for (i = 0; i < H->TableSize; i++) 44. { 45. H->TheLists[i] = malloc(sizeof(struct ListNode)); 46. if (H->TheLists[i] == NULL) 47. { 48. FatalError("Out of space"); 49. } 50. else 51. { 52. H->TheLists[i]->Next = NULL; 53. } 54. 55. } 56. return H; 57.} 58. 59.//查找 60.Position Find(int Key, HashTable H) 61.{ 62. Position P; 63. List L; 64. //獲取表頭 65. L = H->TheLists[Hash(Key, H->TableSize)]; 66. P->Next; 67. while (P!=NULL&&P->Element!=Key) 68. { 69. P->Next; 70. } 71. return P; 72.} 73. 74.//插入 75.void Insert(int Key, HashTable H) { 76. Position Pos, NewCell; 77. List L; 78. Pos = Find(Key, H); 79. if (Pos==NULL) 80. { 81. NewCell = malloc(sizeof(struct ListNode)); 82. if (NewCell == NULL) { 83. 84. } 85. else 86. { 87. L = H->TheLists[Hash(Key, H->TableSize)]; 88. NewCell->Next = L->Next; 89. NewCell->Element = Key; 90. L->Next = NewCell; 91. } 92. } 93.}
開放定址法
分離鏈接散列算法的缺點是需要指針,由於給新單元分配地址需要時間,因此這就導致算法的速度有些減慢,同時算法還要對另一種數據結構的實現。
在開放定址散列算法中,如果有沖突發生,那么就要嘗試選擇另外的單元,直到找出空單元為止。
1.#include <stdio.h> 2.typedef unsigned int Index; 3.typedef Index Position; 4. 5.struct HashTbl; 6.typedef struct HashTbl *HashTable; 7. 8. 9.enum KindOfEntry { Legitimate, Empty, Deleted }; 10. 11.struct HashEntry 12.{ 13. int Element; 14. enum KindOfEntry Info; 15.}; 16. 17.typedef struct HashEntry Cell; 18. 19.struct HashTbl 20.{ 21. int TableSize; 22. Cell *TheCells; 23.}; 24. 25. 26.//初始化 27.HashTable InitializeTable(int TableSize) { 28. HashTable H; 29. int i; 30. if (TableSize < 100) 31. { 32. Error(""); 33. return NULL; 34. } 35. H = malloc(sizeof(struct HashTbl)); 36. if (H == NULL) 37. { 38. FatalError(""); 39. } 40. H->TableSize = NextPrime(TableSize); 41. H->TheCells = malloc(sizeof(Cell)*H->TableSize); 42. if (H->TheCells == NULL) 43. { 44. FatalError("!!!"); 45. } 46. for (i = 0; i < H->TableSize; i++) 47. { 48. H->TheCells[i].Info = Empty; 49. } 50. return H; 51.} 52. 53. 54.Position Find(int Key, HashTable H) { 55. Position CurrentPos; 56. int CollisitionNum; 57. CollisitionNum = 0; 58. CurrentPos = Hash(Key, H->TableSize); 59. while (H->TheCells[CurrentPos].Info != NULL&&H->TheCells[CurrentPos].Element != Key) 60. { 61. CurrentPos += 2 * ++CollisitionNum - 1; 62. if (CurrentPos >= H->TableSize) 63. { 64. CurrentPos -= H->TableSize; 65. } 66. } 67. return CurrentPos; 68.} 69. 70. 71.void Insert(int Key, HashTable H) 72.{ 73. Position Pos; 74. Pos = Find(Key, H); 75. if (H->TheCells[Pos].Info=Legitimate) 76. { 77. H->TheCells[Pos].Info = Legitimate; 78. H->TheCells[Pos].Element = Key; 79. } 80.} 81. 82.//再散列 83.HashTable Rehash(HashTable H) 84.{ 85. int i, OldSize; 86. Cell *OldCells; 87. 88. OldCells = H->TheCells; 89. OldSize = H->TableSize; 90. 91. 92. H = InitializeTable(2 * OldSize); 93. 94. for (i=0; i < OldSize; i++) 95. { 96. if (OldCells[i].Info == Legitimate) 97. { 98. Insert(OldCells[i].Element, H); 99. } 100. } 101. free(OldCells); 102. return H; 103.}
再散列
原始表達到某一個邊際值的時候,建立另外一個大約2倍大的表,掃描整個原始表,計算每個元素的新散列值並插入到新表中。
再散列是一種非常昂貴的操作。
實現方法:
- 只要表填滿到一半就再散列
- 只要當插入失敗就再散列
- 當表到達某一個裝填因子時進行在散列
再散列還可以用在其他數據結構中,比如:隊列。
可擴散列
和B-樹類似。