散列表

散列表

概念引入

现有正整数数组N[8,7,6,4,3,2,1]和M[7,6,5],找出重复的元素。暴力的做法是,对每个要查找的数x,遍历所有N数组,看是有有相等的。这种做法的时间复杂度是O(n*m),当n,m很大的时候O(n^2),这显然是无法接受的。而散列表是算法在空间 和 时间作出权衡的经典例子,它采用一个映射函数,将关键字映射并记录在存储位置,从而在想要查找该记录时,可以直接根据关键字和映射关系计算出该记录在表中存储的位置。

回到最初引入的问题,假设一个bool型数组hashtable[n],其中hashtable[x] == true 表示元素x在数组N中出现过,反之亦然。这样在一开始读入数组N中的正整数时就进行预处理,即当读入x时,令hashtable[x] =true。至此,对数组M中预查询的数,就能直接通过hashtable数组判断每个数是否出现过。显然这种做法的时间复杂度为O(n + m)。

// 找出100以内两数组中重复的数字。这里为了更好的展示逻辑规定a数组比b数组长
public static List repeatNum(int[] a, int[] b) {
        List<Integer> result = new ArrayList<>();
        boolean[] hashTable = new boolean[100];
        int aLength = a.length;
        int bLength = b.length;

        for (int i = 0; i < aLength; i++) {
            hashTable[a[i]] = true;
        }
        IntStream.range(0, bLength)
                .filter(i -> hashTable[b[i]])
                .forEach(i -> {
                    if (aLength > bLength) result.add(b[i]);
                    else result.add(a[i]);
                });
        return result;
}

小结:上面的例子,直接把输入的数作为数组的下标来对这个数的性质进行统计。此时它的查询复杂度为O(1) !

但是,如果是数组中的整数是10^9或者是一个字符串,就不能做数组下标了。这时候就需要散列函数F : key —> address 将关键字映射到存储位置,也就是将元素通过一个函数转换为整数,使得该整数可以尽量唯一的代表这个元素。

Hash函数设计

1. 直接定位法

即上例中所用到的方法,去关键字或关键字的某个线性函数为hash地址,address(key) = a * key + b。

2. 平方取中法

对关键字进行平方计算,取结果的中间几位为hash地址。

3. 折叠法

将关键字拆成几部分,然后将这几部分组合在一起。如,图书ISBN号为8906-241-23,则address(key) = 89+03+24+13+3为hash地址。

4. 除留取余法

如知道hash表的最大长度为m,可以取不大于m的最大质数P,然后对关键字进行取余运算。

address(key) = key % p ,p选的好的话能够最大程度减少冲突。通过除留取余法可能会有两个不同的数key1和key2,使得算出来的地址是一样的,这样一件占据了地址,key2就不能使用了。这样的冲突在所难免,就需要解决冲突。

解决冲突

其中方法1,2 都计算了新的hash值,又称为开放定址法。

1. 线性探查法(Linear Probing)

当得到key的hash值H(key)已被其他元素使用了,那么就检查H(key) + 1是否被占用。如果没有就使用这个位置。否则继续加1,并查看下一个位置是否被占用。这种做法容易导致表中连续若干个位置都被使用,在一定的程度上降低了效率。

2. 平方探查法(Quadratic Probing)

当下标为H(key)的位置被占用时,将按下面的顺序检查表中的位置:H(key) + 1^2 ,H(key) - 1^2, H(key) + 2^2,H(key) - 2^2 ……

3. 链地址法(拉链法)

链地址法不计算新的hash值,而是把所有H(key)相同的key连接成一条单链表。于是当多个关键字key的hash都是h时,就可以直接把这些冲突的key用单链表连接起来,遍历这个单链表就可以找到所有H(key) = h的key。

散列表的应用

  1. 找出两个文件重复的元素
  2. 找出两个文件出现最多的元素
  3. 路由算法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值