• java哈希表(线性探测哈希表。链式哈希表)


    哈希表(散列表)

    通过哈希函数使元素的存储位置与它 的关键码之间能够建立一一映射的关系,在查找时可以很快找到该元素。 

    哈希表hash table(key,value) 的做法其实很简单,就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。


    1.哈希冲突

    就是键(key)经过hash函数得到的结果作为地址去存放当前的键值对,但是却发现该地址已经有人先来了就会产生冲突。这个冲突就是hash冲突了。

    2.load factoe 装载因子

    已占用桶的个数/桶的总数

    当装载因子大于 0.8时就应该扩容。 

    由于哈希冲突的存在造成哈希表的增删查时间复杂度只能无限趋近于0(1)

    3. 哈希冲突的解决

    1.可以把key存放在表中的“下一个“”空位置。即从发生冲突的位置开始,依次向后探测,直到找到空位置为止(线性探测)。 

     2.实现链式哈希表,从根本上说是由一组链表构成。每个链表都可以看做是一个“桶”,我们将所有的元素通过散列的方式放到具体的不同的桶中。插入元素时,首先将其键传入一个哈希函数(该过程称为哈希键),函数通过散列的方式告知元素属于哪个“桶”,然后在相应的链表头插入元素。查找或删除元素时,用同样的方式先找到元素的“桶”,然后遍历相应的链表,直到发现我们想要的元素。因为每个“桶”都是一个链表,所以链式哈希表并不限制包含元素的个数。然而,如果表变得太大,它的性能将会降低。

    3.优点增删改查O(1)

       缺点1.占用内存比较大
                  2.元素没有任何顺序

    4.源代码:

    线性探测哈希表

    class LinerHashMap<T extends Comparable<T>>{
        // 散列表数组
        private Entry<T>[] hashTable;
        // 被占用的桶的个数
        private int usedBucketNum;
        // 哈希表的装载因子
        private double loadFactor;
        // 定义素数表
        private static int[] primTable;
        // 记录当前使用的素数的下标
        private int primIndex;
    
        // 类的静态初始化块
        static{
            primTable = new int[]{3, 7, 23, 47, 97, 127};
        }
    
        /**
         * 构造函数,初始化
         */
        public LinerHashMap(){
            this.primIndex = 0;
            this.hashTable = new Entry[primTable[this.primIndex]];
            this.usedBucketNum = 0;
            this.loadFactor = 0.75;
        }
    
        /**
         * 增加元素
         * @param key
         */
        public void put(T key){
            // 计算哈希表是否需要扩容
            double ret = this.usedBucketNum*1.0 / this.hashTable.length;
            if(ret > this.loadFactor){
                resize(); // 哈希表的扩容
            }
    
            // 先计算key应该放的桶的下标
            int index = key.hashCode() % this.hashTable.length;
            int idx = index;
            do{
                // 表示是从未使用过的桶
                if(this.hashTable[idx] == null){
                    this.hashTable[idx] = new Entry<>(key, State.USING);
                    this.usedBucketNum++;
                    return;
                }
    
                // 表示使用过的桶
                if(this.hashTable[idx].getState() == State.USED){
                    this.hashTable[idx].setData(key);
                    this.hashTable[idx].setState(State.USING);
                    this.usedBucketNum++;
                    return;
                } else {
                    // 正在使用中的桶,不插入重复元素
                    if(this.hashTable[idx].getData().compareTo(key) == 0){
                        return;
                    }
                }
                idx = (idx+1)%this.hashTable.length;
            } while(idx != index);
        }
    
        /**
         * 哈希表的扩容函数
         */
        private void resize() {
            Entry<T>[] oldHashTable = this.hashTable;
            this.hashTable = new Entry[primTable[++this.primIndex]];
            this.usedBucketNum = 0;
    
            for (int i = 0; i < oldHashTable.length; i++) {
                if(oldHashTable[i] != null
                        && oldHashTable[i].getState() == State.USING){
                    this.put(oldHashTable[i].getData());
                    this.usedBucketNum++;
                }
            }
        }
    
        /**
         * 删除元素
         * @param key
         */
        public void remove(T key){
            // 先计算key应该放的桶的下标
            int index = key.hashCode() % this.hashTable.length;
    
            // 从当前位置开始找元素
            int idx = index;
            do{
                // 如果遍历桶的过程中,发现了从未使用过的桶,直接返回
                if(this.hashTable[idx] == null){
                    return;
                }
                if(this.hashTable[idx].getState() == State.USING
                        && this.hashTable[idx].getData().compareTo(key) == 0){
                    this.hashTable[idx].setData(null);
                    this.hashTable[idx].setState(State.USED);
                    this.usedBucketNum--;
                    return;
                }
                idx = (idx+1)%this.hashTable.length;
            } while(idx != index);
        }
    
        /**
         * 查询元素  返回key的值,找不到返回null
         * HashMap
         * @param key
         * @return
         */
        public T get(T key){
            // 先计算key应该放的桶的下标
            int index = key.hashCode() % this.hashTable.length;
    
            // 从当前位置开始找元素
            int idx = index;
            do{
                // 如果遍历桶的过程中,发现了从未使用过的桶,直接返回
                if(this.hashTable[idx] == null){
                    return null;
                }
                if(this.hashTable[idx].getState() == State.USING
                        && this.hashTable[idx].getData().compareTo(key) == 0){
                    return key;
                }
                idx = (idx+1)%this.hashTable.length;
            } while(idx != index);
    
            return null;
        }
    
        /**
         * 定义桶的状态值
         */
        static enum State{
            UNUSE,// 桶从未使用过
            USED,// 桶被用过了
            USING// 桶正在使用中
        }
    
        /**
         * 定义桶的元素类型
         * @param <T>
         */
        static class Entry<T extends Comparable<T>>{
            T data;
            State state;
    
            public Entry(T data, State state) {
                this.data = data;
                this.state = state;
            }
    
            public T getData() {
                return data;
            }
    
            public void setData(T data) {
                this.data = data;
            }
    
            public State getState() {
                return state;
            }
    
            public void setState(State state) {
                this.state = state;
            }
        }
    }

    链式探测哈希表

    public class LinkHashTable<K extends Comparable<K>,V> {
    
        // 哈希桶
        private Entry<K,V>[] table;
        // 装载因子 0.75
        private double loadFactor;
        // 记录已经占用的桶的数量
        private int usedBucketSize;
    
        /**
         * 哈希表初始化
         */
        public LinkHashTable(){
            this.table = new Entry[3];
            this.loadFactor = 0.75;
            this.usedBucketSize = 0;
        }
    
        /**
         * 给哈希表增加元素
         * @param key
         * @param value
         */
        public void put(K key, V value){
            if(this.usedBucketSize*1.0/this.table.length>this.loadFactor){
                this.expand();
            }
            int idx = key.hashCode() % this.table.length;
            if(this.table[idx]==null){
                this.table[idx]=new Entry<>(key,value,null);
                this.usedBucketSize++;
                return;
            }
            Entry<K,V>entry=this.table[idx];
            //判断是否有这个key,如果有直接替换
            while (entry!=null){
                if(entry.key.compareTo(key)==0){
                    entry.value=value;
                    return;
                }
                entry=entry.next;
            }
            this.table[idx]=new Entry<>(key,value,this.table[idx]);
        }
    
        /**
         * 在哈希表中查询key是否存在,如果key存在,返回它对应的value值,
         * 否则返回null
         * @param key
         * @return
         */
        public V get(K key){
            int idx = key.hashCode() % this.table.length;
            if(this.table[idx]==null){
                return null;
            }
            Entry<K,V>entry=this.table[idx];
               while (entry!=null){
                   if(entry.key.compareTo(key)==0){
                       return entry.value;
                   }
                   entry=entry.next;
               }
            return null;
        }
    
        /**
         * 删除哈希表中key值为参数指定的节点
         * @param key
         */
        public void remove(K key){
            int index = key.hashCode() % this.table.length;
            if(this.table[index]==null){
                return;
            }else if(this.table[index].key.compareTo(key)==0){
                this.table[index]=this.table[index].next;
                return;
            }
            Entry<K,V>entry=this.table[index];
            Entry<K,V>entry1=entry.next;
            if(entry1!=null){
                if(entry1.key.compareTo(key)==0){
                    entry.next=entry1.next;
                }
                entry=entry.next;
                entry1=entry1.next;
            }
            if(this.table[index]==null){
                this.usedBucketSize--;
            }
        }
    
        /**
         * 哈希表的扩容函数
         */
        private void expand() {
            Entry<K, V>[] oldTable = this.table;
            this.table = new Entry[oldTable.length * 2 + 1];
            this.usedBucketSize = 0;
            for (int i = 0; i < oldTable.length; i++) {
                if (oldTable[i] != null) {
                    this.put(oldTable[i].key, oldTable[i].value);
                }
            }
        }
        /**
         * 链式哈希表中节点的类型
         * @param <K,V>
         */
        static class Entry<K extends Comparable<K>,V> {
            K key;  // student id
            V value; // student
            Entry<K, V> next;
    
            public Entry(K key, V value, Entry<K, V> next) {
                this.key = key;
                this.value = value;
                this.next = next;
            }
        }
    }
  • 相关阅读:
    什么是HTTP
    通过递归法解决阶梯问题(n个台阶,上楼可以一步上1阶,也可以一步上2阶,一共有多少种上楼的方法)
    在Intelli Idea中使用plantuml(plantuml时序图的使用)
    Java中if(boolean)与if(boolean=true)的区别
    实现一个Servlet程序
    退出mysql的编辑模式
    mysql数据库基本操作命令行
    通过mysql命令查看mysql服务实例支持的搜索引擎
    Mac环境下使用终端启动Mysql,并进行mysql数据库的连接
    路飞学城Python-Day4
  • 原文地址:https://www.cnblogs.com/jiezai/p/11032306.html
Copyright © 2020-2023  润新知