• 查找算法—散列表


    散列表

    散列表在查找当中使用非常广泛(本文当然也还会说明删除等其他操作)
    本文主要说明散列表的两种解决冲突的办法

    散列表查找

    散列表:对于很多复杂类型的键值,我们可以用一个数组来实现无序的符号表,将键值通过散列函数转化作为数组的索引,查找即对数组进行查找(这里就需要解决冲突)。

    散列表的实现分为两步:
    1. 散列函数:
    将数据的键值转化为数组的一个索引的一个函数。理想情况下,当然是所有的键值,均对应不同的数组索引。但是更多的是,多个不同的键值,通过散列函数之后,有了相同的数组索引,这时候就需要解决这些同意数组索引的不同键值的确定问题。
    2. 解决冲突
    这是对于上一步骤产生相同的数组索引问题的解决,主要有两种解决冲突的办法。
    1>拉链法
    2>线性探测法

    散列函数

    对于不同类型的数据,构造不同的散列函数,当然是最好的。
    下面主要是三种常用的散列函数的构造:

    1. 平方取中法
      ——先通过求关键字的平方值扩大相近数的差别,然后根据表长度取中间的几位数作为散列函数值。
      ——这种方式中间的几位数都和关键字的每一位都有关,产生的散列地址较为的均匀。
    2. 折叠法
      ——将关键字分割成相同的几位数(最后一位可不同),然后去这几部分的叠加和。
      ——折叠法一般是和除留余法一起使用的。
    3. 除留余数法
      ——取关键字被某个不大于散列表表长 m 的数 p 除后所得的余数为散列地址。即 hash(k) = k mod p, p < m。不仅可以对关键字直接取模,也可在折叠法、平方取中法等运算之后 取模。
      ——对 p 的选择很重要,一般取素数或 m ,若 p 选择不好,容易产生碰撞。

    对于一些常用的数据类型:如正整数,浮点数,字符串,组合键等。
    1>正整数:最常用的就是除留余数法。
    2>浮点数:通常是将键值表示为二进制,再使用除留余数法。(Java就是这样处理的)
    3>字符串:除留余数法,将字符串的每个字符使用charAt()转化为char值,即是一个非负的16位整数。这种办法确保了字符串的每个字符都能起到作用。
    4>组合键:与处理String类似,将每个键值都混合起来,在进行散列函数的处理。

    拉链法

    1. 拉链法原理
      将大小为M的数组中的每个索引都指向一个链表,这样对于产生冲突的元素,就都存储在了数组对应索引的链表中。这样查找时,就需要先找到数组对应的索引中,再对索引中的链表进行顺序查找,就能找到目标元素。
      拉链法
    2. Java实现(只贴关键代码)
            //put(Key,Value)
            // double table size if average length of list >= 10
            if (n >= 10*m) resize(2*m);
    
            int i = hash(key);
    
            //judege key is not null
            if (!st[i].contains(key))
                n++;
            st[i].put(key, val);
            //delete(Key)
            int i = hash(key);
            if (st[i].contains(key)) n--;
            st[i].delete(key);
    
            // halve table size if average length of list <= 2
            if (m > INIT_CAPACITY && n <= 2*m) resize(m/2);

    线性探测法

    1. 线性探测法原理
      使用大小为M的数组保存N个键值对,用数组中的空位来解决冲突的办法。
      当发生碰撞时(数组的索引已经被前面的键值所占有),我们直接检查散列表的下一个位置(将索引值+K),这样的线性探测,只会有三种结果:
      1>命中,该位置的键与查找键相同;
      2>未命中,键空;
      3>未命中,键值不相同继续查找,索引+K。
      如图:
      线性探测法

      其中K值不同,也分为不同的线性探测法:
      ( 1 ) d i = 1 , 2 , 3 ,——线性探测再散列;
      ( 2 ) d i = 1^2 ,- 1^2 , 2^2 ,- 2^2 , k^2, -k^2,——二次探测再散列;
      ( 3 ) d i = 伪随机序列 ,——伪随机再散列;

    2. Java实现
            //put(Key,Value)
            // double table size if 50% full
            if (n >= m/2) resize(2*m);
    
            int i;
            for (i = hash(key); keys[i] != null; i = (i + 1) % m) {
                if (keys[i].equals(key)) {
                    vals[i] = val;
                    return;
                }
            }
            keys[i] = key;
            vals[i] = val;
            n++;
            //delete(Key)
            // find position i of key
            int i = hash(key);
            while (!key.equals(keys[i])) {
                i = (i + 1) % m;
            }
    
            // delete key and associated value
            keys[i] = null;
            vals[i] = null;
    
            // rehash all keys in same cluster
            i = (i + 1) % m;
            while (keys[i] != null) {
                // delete keys[i] an vals[i] and reinsert
                Key   keyToRehash = keys[i];
                Value valToRehash = vals[i];
                keys[i] = null;
                vals[i] = null;
                n--;
                put(keyToRehash, valToRehash);
                i = (i + 1) % m;
            }
    
            n--;
    
            // halves size of array if it's 12.5% full or less
            if (n > 0 && n <= m/8) resize(m/2);
            assert check();
  • 相关阅读:
    js事件冒泡替我背了很多黑锅 嘿嘿
    opencvmin函数
    关于Block Formatting Context--BFC和IE的hasLayout
    javascript面向对象包装类Class的类库解析
    nodejs中exports与module.exports的区别
    ie6固定定位层fixed
    CSS的类名交集复合选择器
    遮罩层覆盖整个页面
    nodejs的require模块及路径
    struts.properties配置详解
  • 原文地址:https://www.cnblogs.com/l0zh/p/13739768.html
Copyright © 2020-2023  润新知