查找算法大总结: http://www.cnblogs.com/maybe2030/p/4715035.html#_label6
常用的hash函数: http://blog.csdn.net/mycomputerxiaomei/article/details/7641221
什么是哈希表(Hash)?
我们使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函数, 也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标)相对应,于是用这个数组单元来存储这个元素;也可以简单的理解为,按照关键字为每一个元素"分类",然后将这个元素存储在相应"类"所对应的地方。但是,不能够保证每个元素的关键字与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了"冲突",换句话说,就是把不同的元素分在了相同的"类"之中。后面我们将看到一种解决"冲突"的简便做法。
总的来说,"直接定址"与"解决冲突"是哈希表的两大特点。
什么是哈希函数?
哈希函数的规则是:通过某种转换关系,使关键字适度的分散到指定大小的的顺序结构中,越分散,则以后查找的时间复杂度越小,空间复杂度越高。
算法思想:哈希的思路很简单,如果所有的键都是整数,那么就可以使用一个简单的无序数组来实现:将键作为索引,值即为其对应的值,这样就可以快速访问任意键的值。这是对于简单的键的情况,我们将其扩展到可以处理更加复杂的类型的键。
算法流程:
1)用给定的哈希函数构造哈希表;
2)根据选择的冲突处理方法解决地址冲突;
常见的解决冲突的方法:拉链法和线性探测法。详细的介绍可以参见:浅谈算法和数据结构: 十一 哈希表。
3)在哈希表的基础上执行哈希查找。
哈希表是一个在时间和空间上做出权衡的经典例子。如果没有内存限制,那么可以直接将键作为数组的索引。那么所有的查找时间复杂度为O(1);如果没有时间限制,那么我们可以使用无序数组并进行顺序查找,这样只需要很少的内存。哈希表使用了适度的时间和空间来在这两个极端之间找到了平衡。只需要调整哈希函数算法即可在时间和空间上做出取舍。
实现:
定义hash:
1 typedef struct node 2 { 3 int value; 4 struct node *next; 5 }NODE; 6 7 typedef struct hash 8 { 9 int size; 10 NODE **list; 11 }HASH;
初始化hash:
1 HASH *HashInit(int size) 2 { 3 if (size <= 0) 4 return NULL; 5 HASH *hash = (HASH*)malloc(sizeof(HASH)); 6 if (NULL == hash) 7 return NULL; 8 hash->size = size; 9 hash->list = (NODE**)malloc(sizeof(NODE*) * hash->size); 10 if (NULL == hash->list) 11 { 12 free(hash); 13 hash = NULL; 14 return NULL; 15 } 16 int i; 17 for (i = 0; i < size; i++) 18 { 19 hash->list[i] = (NODE*)malloc(sizeof(NODE)); 20 hash->list[i]->next = NULL; 21 } 22 return hash; 23 }
hash查找:
NODE *HashFind(HASH *hash, int key) { NODE *list = hash->list[HashFun(key, hash->size)]; NODE *node = list->next; for (;NULL != node && node->value != key; node = node->next); return node; }
hash插入:
void HashInstert(HASH* hash, int key) { if (NULL != HashFind(hash, key)) return; NODE *node = (NODE*)malloc(sizeof(NODE)); if (NULL == node) return; NODE *list = hash->list[HashFun(key, hash->size)]; node->value = key; node->next = list->next; list->next = node; }