介绍:
哈希查找算法又称散列查找算法,是一种借助哈希表(散列表)查找目标元素的方法,查找效率最高时对应的时间复杂度为 O(1)。哈希查找算法既支持有序数列也支持无序数列,是比较高效的查找算法。
算法思想:
首先需要知道,哈希查找是在哈希表中来查找目标元素的,所以需要先创建一个哈希表,但是哈希表又是什么呢?哈希表是在数组的基础上构建的,
使用数组构建哈希表,最大的好处在于:可以直接将数组下标当作已存储元素的索引,不再需要为每个元素手动配置索引,极大得简化了构建哈希表的难度。
我们知道,在一个数组中如果我们知道了目标元素的在数组中的下标的话就会很容易的找到该元素,而哈希表的解决方案是:数组中的各个元素并不是按照数组的起始位置进行存储的,他们的下标而是按照已经设计好的函数计算出来的,这个函数就是哈希函数。
这个哈希函数就相当于数学中的一元一次方程一样,我们给它一个值,则它会返回一个值给我们,此时这个值就是该元素存储在哈希表中的下标。例如:将 {20, 30, 50, 70, 80} 存储到哈希表中,我们设计的哈希函数为 y=x/10,最终各个元素的存储位置如下图所示:
但是,这个时候就会又有疑问,假设我设计的哈希函数是y = x % 10 怎么办呢?此时各个元素的存储位置就会是这样的:
此时一个下标中对应着多个元素, 这样存储位置就发生了冲突,我们就称这种状态为哈希冲突或者是哈希碰撞。所以此时也提供了一些解决方案:线性探测法,再哈希法,链地址法等。所以由此可以看的出来设计一个好的哈希函数可以减少哈希冲突的出现。
使用线性探测法解决哈希冲突的过程是:
- 元素 5 最先存储到数组中下标为 5 的位置;
- 元素 20 最先存储到数组中下标为 0 的位置;
- 元素 30 的存储位置为 0,和 20 冲突,根据线性探测法,从下标为 0 的位置向后查找,下标为 1 的存储位置空闲,用来存储 30;
- 元素 50 的存储位置为 0,和 20 冲突,根据线性探测法,从下标为 0 的位置向后查找,下标为 2 的存储位置空闲,用来存储 50;
- 元素 55 的存储位置为 5,和 5 冲突,根据线性探测法,从下标为 5 的位置向后查找,下标为 6 的存储位置空闲,用来存储 55。
借助线性探测法,最终 {5, 20, 30, 50, 55} 存储到哈希表中的状态为:
假设我们从图 4 所示的哈希表中查找元素 50,查找过程需要经过以下几步:
- 根据哈希函数 y=x%10,目标元素的存储位置为 0,但经过和下标为 0 处的元素 20 比较,该位置存储的并非目标元素;
- 根据线性探测法,比较下标位置为 1 处的元素 30,也不是目标元素;
- 继续比较下标位置为 2 的元素 50,成功找到目标元素。
代码展示:
#include <stdio.h>
#define N 10 //指定哈希表的长度
//自定义哈希函数
int hash(int value) {
return value % 10;
}
//创建哈希表
void creatHash(int arr[5], int hashArr[N]) {
int i,index;
//将序列中每个元素存储到哈希表
for (i = 0; i < 5; i++) {
index = hash(arr[i]);
while(hashArr[index % N] != 0) {
index++;
}
hashArr[index] = arr[i];
}
}
//实现哈希查找算法,hashArr 表示哈希表,value 为要查找的目标元素
int hash_search(int* hashArr, int value) {
int hashAdd = hash(value); //查找目标元素所在的索引
while (hashArr[hashAdd] != value) { // 如果索引位置不是目标元素,则发生了碰撞
hashAdd = (hashAdd + 1) % N; // 根据线性探测法,从索引位置依次向后探测
//如果探测位置为空,或者重新回到了探测开始的位置(即探测了一圈),则查找失败
if (hashArr[hashAdd] == 0 || hashAdd == hash(value)) {
return -1;
}
}
//返回目标元素所在的数组下标
return hashAdd;
}
int main()
{
int hashAdd;
int hashArr[N] = { 0 };
int arr[5] = { };
creatHash(arr, hashArr);
hashAdd = hash_search(hashArr, 50);
//如果返回值为 -1,表明查找失败,反之则返回目标元素所在的位置
if (hashAdd == -1) {
printf("查找失败\n");
}
else {
printf("查找成功,目标元素所在哈希表中的下标为:%d", hashAdd);
}
return 0;
}