数据结构之散列
散列是一种用于以常数平均时间执行插入,删除和查找的技术
一般想法
一个关键字就是一个带有相关值的字符串。我们把表大小记作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-树类似。
如本文对您有帮助请移步右下角,推荐本文,谢谢大家的点赞,因为您的支持是我最大动力