散列表
如果表够大,散列函数足够好,那么散列表在查找上具有O(1)的时间复杂度。
但是我们考虑出现冲突的情况,如果使用分离链表法来处理冲突。那么链表的平均长度等于装填因子a(元素个数与散列表大小的比值)的大小。所以不成功查找的复杂度为a,成功查找的复杂度为1 + a/2。
代码实现如下:
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cmath> 4 #include <cstring> 5 // 分离链接散列表 6 typedef unsigned int index; 7 typedef int itemType; 8 typedef struct ListNode *position; 9 typedef position list; 10 typedef struct HashTable *hashTable; 11 12 struct ListNode { 13 itemType item; 14 position next; 15 }; 16 struct HashTable { 17 int tableSize; 18 list *listArr; 19 }; 20 21 int 22 NextPrime(int n) 23 { 24 int ceiling, isPrime; 25 26 isPrime = 0; 27 ceiling = 2 * n; 28 29 for ( ; n < ceiling; n++) { 30 int nSqrt = (int)sqrt(n); 31 isPrime = 1; 32 for (int i = 2; isPrime && i <= nSqrt; i++) 33 { 34 if (n % i == 0) { 35 isPrime = 0; 36 } 37 } 38 if (isPrime) { 39 return n; 40 } 41 } 42 } 43 44 index 45 Hash(itemType key, int tableSize) 46 { 47 return key % tableSize; 48 } 49 50 hashTable 51 InitializeTable(int tableSize) 52 { 53 hashTable h; 54 55 h = (hashTable)malloc(sizeof(struct HashTable)); 56 if (h == NULL) { 57 printf("Out of space!\n"); 58 exit(-1); 59 } 60 61 h->tableSize = NextPrime(tableSize); 62 63 h->listArr = (list*)malloc(sizeof(list) * h->tableSize); 64 // 65 if (h->listArr == NULL) { 66 printf("Out of space!\n"); 67 exit(-1); 68 } 69 70 memset(h->listArr, 0, h->tableSize * sizeof(list*)); 71 72 return h; 73 } 74 75 position 76 Find(itemType key, hashTable h) 77 { 78 list l; 79 80 l = h->listArr[Hash(key, h->tableSize)]; 81 82 for ( ; l != NULL; l = l->next) { 83 //printf("%d\n", Hash(key, h->tableSize)); 84 if (l->item == key) { 85 //printf("here\n"); 86 return l; 87 } 88 } 89 90 return NULL; 91 } 92 93 void 94 Insert(itemType key, hashTable h) 95 { 96 position p, newNode; 97 list l; 98 99 p = Find(key, h); 100 101 int hValue = Hash(key, h->tableSize); 102 103 if (p == NULL) { 104 newNode = (position)malloc(sizeof(struct ListNode)); 105 if (newNode == NULL) { 106 printf("Out of space!\n"); 107 exit(-1); 108 } 109 /* 错误的方法,只改变了 l 这个指针,却没变动listArr[hValue]本身。 110 list l = h->listArr[hValue]; 111 newNode->next = l; 112 newNode->item = key; 113 l = newNode; 114 //h->listArr[hValue] = newNode; 115 */ 116 117 newNode->item = key; 118 newNode->next = h->listArr[hValue]; 119 h->listArr[hValue] = newNode; 120 printf("key %d inserted at position %d.\n", h->listArr[hValue]->item, Hash(key, h->tableSize)); 121 122 } 123 } 124 125 int 126 main(int argc, char** argv) 127 { 128 hashTable h; 129 130 h = InitializeTable(10); 131 132 for (int i = 0; i < 10; i++) { 133 Insert(i*i, h); 134 } 135 136 137 for (int i = 0; i < 100; i++) { 138 if (Find(i, h)) { 139 printf("key: %d found.\n", i); 140 } else { 141 printf("key: %d not found.\n", i); 142 } 143 } 144 145 146 system("pause"); 147 return 0; 148 }
-
另外,当我们使用开放定址法来处理冲突的时候,特别要注意删除的时候,不能直接删除。原因是如果你删除了P这个位置的元素x。那么如果以前有的元素y应该映射到P,却因为冲突而到了其他地方。当x被删除之后,P这个位置为空。那么如果我们要寻找y,就会第一个找到P这个位置,发现为空。于是就找不到y了。(可以继续遍历开放定址法的位置来寻找y……但是这样子不是太楞了吗?如果原本就没y这个元素呢,那我们得做许多无用功。)
不能直接删除,所以一般我们使用懒惰删除,给P位置一个“已删除”的标记,就能解决这个问题了。