zoukankan      html  css  js  c++  java
  • 答客难——散列九问(hash table)

    综述:

    散列是一种码位一体化搜索解决方案

    它将元素关键码(key)映射到实际存储位置附近,实现了事实上的分治。搜索复杂度接近O(1)。

    只要选择好合适的散列函数和冲突解决办法,散列就快速,高效,优雅。

    散列函数以除数留余法最佳,冲突解决方法以开散列法最好。

    客:散列背后的思想是什么?

    主:其他搜索解决方案都是从搜索树出发,尽可能减少搜索树的深度。散列与它们截然不同!

    散列是借鉴了数组的思想。数组为什么查找高效?因为它有索引,直接标志了存储位置。

    那么如果我们把元素的关键码作为索引,也就能取得和数组一样的搜索速度。

    这也就是我说的码位一体化。

    当然实际实现要解决很多问题,比如冲突,不过散列很多情况下都是最高效的搜索数据结构。

    客:散列有什么作用?

    主:散列是一种码位一体化解决方案。为了解决快速查找与插入元素的问题。

    客:它为什么快?

    主:散列将待查找元素的标志码(key)映射到具体的存储空间附近(一般是数组),把搜索整个存储空间变为搜索冲突区域,

    由于冲突区域远小于存储空间,这样就实现了事实上的分治。散列的快速也来源于此。实际上,只要冲突不多,散列的查找复杂度接近O(1)。

    客:什么是散列函数?

    主:hash在英文中是“弄乱”意思。散列函数就是把元素的关键码,均匀的映射到存储空间,以利于查找。

    客:常见散列函数有哪些?

    主:除留余数法,数字分析法,平方取中法,折叠法。其中除留余数法最好。

    除留余数法也就是选一个小于等于存储空间大小的最大质数,然后将关键码对其求余(%),将余数作为存储空间。

    客:什么是冲突?

    主:由于映射不可能均匀,总会出现将两个不同元素映射到同一存储位置的情况出现,这就是冲突。

    客:怎么解决冲突?

    主:这要根据实际存储实现来定。闭散列存储实现(也就是用数组)解决冲突方法就是继续查看下一存储位置,如果空就插入,不空就继续查看。

    所以当散列很满时,映射位置往往不是实际存储位置。这也是我为什么说实际存储位置在映射位置附近的原因。不过如果冲突探测方法均匀的话,冲突区域远小于整体存储空间,散列仍然高效。

    因此,闭散列的冲突探测函数就是想把探测位置尽可能分散,避免某位置冲突多而对整体造成影响。

    开散列方法是数组链表混合存储。数组存放每个映射位置的起始地址,链表存放冲突元素。

    开散列方法是最好的冲突解决方法,因为它实现了事实上的冲突映射均匀分布。

    客:闭散列的冲突探测函数有哪些?

    主:有线性探测法,二次探测法,双散列法等。整体来说,解决冲突实际上进行的是二次散列映射,映射的越均匀,效能越好。

    这样是开散列为什么比闭散列好的原因。开散列实现了事实上最好的冲突映射,虽然它是从存储层次解决的。

    客:散列搜索性能与什么有关?

    主:只与装载因子有关。装载因子就是衡量表是否满的标志。取值在0-1之间,0表示空,1表示满。

    装载因子越小,搜索性能越好。闭散列搜索因子最好不要超过0.5,不过如果开散列实现的话,即使装载因子接近1,也能保证很好的搜索性能。

    下面给出闭散列线性探测法的一个实现:

    class Data implements Comparable<Data>{
        int key;
        int value;
        public Data(){}
        public Data(int key, int value){
            this.key = key;
            this.value = value;
        }
        @Override
        public int compareTo(Data data) {
            return this.key - data.key;
        }
    }
    
    /**
     * closed hashTable: stored by cycle array.
     * it is just an example.
     * Note that closed hashTable is not a good implement of hashTable, comparing to open hashTable.
     */
    public class MyHashSet {
        private int setSize;
        private int elementNum;
        private String[] setInfo;
        private Data[] hashSet;
        public MyHashSet(){
            this.setSize = 6;  // the size is small, because it is easy to test :)
            this.elementNum = 0;
            this.hashSet = new Data[this.setSize];
            this.setInfo = new String[this.setSize]; //"empty", "deleted" or "active"
            for (int i = 0; i < setInfo.length; i++) {
                setInfo[i] = "empty";
            }
        }
        private int hash(int key){
            return key % 5;
        }
    
        /**
         * Find the storage position of element
         * This is a private element. because it cannot directly reflect the real position of element.
         * @param element
         * @return position
         */
        private int searchPos(Data element){
            int pos = this.hash(element.key);
            int j = pos;
            do {
                if (setInfo[j].equals("empty") || setInfo[j].equals("active") && hashSet[j].key == element.key){
                    break;
                }
                j = (j + 1) % this.setSize;
            } while (j != pos);
    
            return j;
        }
    
        /**
         * Find the position of element.if find, return position; else, return -1;
         * @param element
         * @return
         */
        public int search(Data element){
            int pos = this.searchPos(element);
            if (setInfo[pos].equals("active") && hashSet[pos].key == element.key){
                return pos;
            }else {
                return -1;
            }
        }
    
        /**
         * insert a new element into hashSet
         * @param element
         * @return
         */
        public boolean insert(Data element){
            if (this.elementNum == this.setSize){
                System.err.println("The Set is FULL!");
                return false;
            }
            int pos = searchPos(element);
            if (!setInfo[pos].equals("active")){
                setInfo[pos] = "active";
                hashSet[pos] = element;
                this.elementNum++;
                return true;
            }else {
                if (hashSet[pos].key == element.key){
                    System.err.println("Already have!");
                    return false;
                }else {
                    System.err.println("The Set is logically full!");
                    return false;
                }
            }
        }
    
        /**
         * delete the element of hashSet
         * @param element
         * @return if delete successfully , return true; else, false. 
         */
        public boolean delete(Data element){
            if (this.elementNum == 0){
                System.err.println("The Set is Empty!");
                return false;
            }
            int pos = searchPos(element);
            if (setInfo[pos].equals("active") && hashSet[pos].key == element.key){
                setInfo[pos] = "deleted";
                this.elementNum--;
                return true;
            }else {
                System.err.println("Not have!");
                return false;
            }
        }
    
        
        public static void main(String[] args){
            MyHashSet hashSet = new MyHashSet();
    
            hashSet.insert(new Data(1,11));
            hashSet.insert(new Data(13,12));
            hashSet.insert(new Data(3,13));
            hashSet.insert(new Data(4,14));
            hashSet.insert(new Data(16,15));
            hashSet.insert(new Data(5,16));
            for (Data d : hashSet.hashSet){
                System.out.println(d.value);
            }
            System.out.println(hashSet.elementNum);
            hashSet.insert(new Data(14,4));
    
        }
    }
  • 相关阅读:
    Visual Studio 2005 不能调试的问题
    自学C语言_第一章
    批处理For循环一键Update补丁程序
    小米MiFlash刷机报错售后方法参考
    Civil 3D 2012 CAD安装完成后打开报错“致命错误:Unhandled Delayload "D3DCOMPILER_47.dll"
    Windows查看端口被占用
    vc ++6.0打开或者添加出现错误解决方案
    一天总结
    一天总结
    一天总结
  • 原文地址:https://www.cnblogs.com/zqiguoshang/p/6593212.html
Copyright © 2011-2022 走看看