前面说的 各种查找都是 基于 “比较” 的基础 来进行 查找的。查找的 效率 要 看 比较的 次数。那么 有没有 不需要 比较,就可以 找到 想要的数据的 方法呢?
哈希表 就是 这样的 一种方法,它用 数组 作为 保存 关键字的 数据原型,通过 一个 哈希 函数f(k),来找到 关键字 存储的位置,从而 找到想要的信息。
例如 我们 想要解决 这样的一个问题:
假设这有一个各种字母组成的字符串,假设这还有另外一个字符串,而且这个字符串里的字母数相对少一些。什么方法能最快的查出所有小字符串里的字母在大字符串里都有?
比如,如果是下面两个字符串:
String
1: ABCDEFGHLMNOPQRS
String
2: DCGSRQPOM
我们
可以用 一个 分配 一个 26 个 int 型的 整形数组 a,将 0~25 分别 代表 A~Z 是否 出现,如果 出现则 值为 1,没有出现
值为0.
则
我们 只需 遍历 String1,然后 将 对应的 元素 设置 为1,然后 遍历 String2 ,如果 查找 过程中 ,遇到了 0 值, 则 不是
。否则 String2
的 字母 在 String1 中 都 存在。
哈希表 虽然 快速,但是 其 数据 原型 基于 数组,同样 有缺陷。
当 查找的 元素 集合 太大,不同的 关键字,却 得到 同样的 地址。即 k1 != k2,, F(K1) = = F(K2),这时 叫做 冲突。冲突 是无法避免的。只能 通过一些方法 减少 冲突。当 我们 插入 元素时,寻找 插入位置,造成的 冲突次数 太多,影响查找效率,我们 只能 重新 建表,这是个 费时的过程。
而且 哈希 是 无法 按 从小到 大 遍历 数据的。
所以 我们在 用哈希的时候得考虑这些:
1.哈希函数
2冲突函数
3初始表长
4冲突多少次,我们就重新建表,
5.是否需要 顺序遍历。
下面代码 用的是
哈希函数:除整取余法
冲突函数:开发定址法(线性)
冲突次数 到达 表长的一半 就重新建表。
哈希表基本结构 ,初始化 和销毁
#include "stdafx.h" #include <cstdlib> int hashSize[] = {11,13,17,19};//哈希表容量增加 数组. #define NULL_KEY 0 struct HashTable{ int * base;//数据的基址 int count;//表的数量 int sizeIndex;//表的容量大小的索引 }; void initHash(HashTable * t){ t->sizeIndex =0; t->base = (int *)calloc(hashSize[t->sizeIndex],sizeof(int)); t->count = 0; } void destoryHash(HashTable * t){ free(t->base); t->base = NULL; t->sizeIndex = 0; t->count = 0; }
哈希函数:
//除留余数法 int hash(HashTable t,int key){ return key % hashSize[t.sizeIndex]; }
冲突函数:
//开发定址 线性探索解决冲突法 int collision(HashTable t,int key,int times){ return (key + times) % hashSize[t.sizeIndex]; }
查找函数:
int search(HashTable t,int key,int * index,int *ctimes){ *index = hash(t,key); *ctimes = 0; while (t.base[*index] != NULL_KEY && t.base[*index] != key){ (*ctimes)++; *index = collision(t,key,*ctimes); } printf("------------查找%d, 查找了%d次--------------\n",key,*ctimes+1); if (t.base[*index] == key){ return t.base[*index]; } else{ return NULL_KEY; } }
插入关键字,以及 重建表函数:
void reCreateHashTable(HashTable * t,int key); void insertHash(HashTable *t,int key){ int index;//插入位置 int ctimes;//冲突次数 int result = search(*t,key,&index,&ctimes); if (result == NULL_KEY && ctimes < hashSize[t->sizeIndex]/2){//没找到 t->base[index] = key; t->count ++; } else{//重新建表 reCreateHashTable(t,key); } } // void reCreateHashTable(HashTable * t,int key){ printf("--------------重建哈希表----------------\n"); int * oldBase = t->base;//保存老空间. int oldSize = hashSize[t->sizeIndex];//老空间的容量大小 t->sizeIndex++; int newSize = hashSize[t->sizeIndex];//新空间大小 t->base = (int *) calloc(newSize,sizeof(int));//新空间 //插入之前将 表的数量置0 t->count = 0; for (int i = 0; i < oldSize; i++){ if (oldBase[i] != NULL_KEY){ insertHash(t,oldBase[i]); } } free(oldBase);//释放老空间 insertHash(t,key);//插入冲突的关键字. }
测试函数:
static int testArray[10] = {1,18,7,55,23,45,98,76,35,29}; int _tmain(int argc, _TCHAR* argv[]) { HashTable table; initHash(&table); for (int i = 0; i < 10; i++){ insertHash(&table,testArray[i]); } int index ,count; for (int i = 0; i < 10; i++){ search(table,testArray[i],&index,&count); } destoryHash(&table); return 0; }
代码工程文件网盘地址:http://pan.baidu.com/s/1kToXLcj