【数据结构】哈希表详解以及代码实现

11 篇文章 0 订阅
5 篇文章 0 订阅

目录

1.来源:

2.哈希函数

1.哈希函数的设计规则

 2.哈希函数的设计思路

3.哈希碰撞

4.解决哈希碰撞的方案

5.负载因子 

3.基于开散列方案的HashMap实现

 1.HashMap类中的属性

2.哈希函数

3.判断当前哈希表中是否含有指定的key值

4.判断当前哈希表中是否包含指定的value值

5.在哈希表中新增,修改元素

6.哈希表扩容

7.删除哈希表中指定的key节点


1.来源:

哈希表来源于数组的随机访问特性

当我们需要查找某个指定元素时,

用链表存储:从链表头遍历到链表尾部,时间复杂度为O(n)

用平衡搜索树存储:时间复杂度为O(logn)

用数组存储,如果知道了元素的索引,那么查找元素的时间复杂度就是O(1)

利用数组的随机访问特性来查找元素,这个思想就是哈希表产生的背景

2.哈希函数

1.哈希函数的设计规则

 2.哈希函数的设计思路

3.哈希碰撞

哈希碰撞是指两个不同的值经过哈希函数计算之后得到相同的值,不论是多优秀的哈希函数,哈希碰撞都不可避免,我们只能尽量降低哈希碰撞出现的概率

取一个素数作为负载因子可以有效降低哈希碰撞的几率

4.解决哈希碰撞的方案

1.闭散列(线性探测):发生冲突时,找冲突旁边的空闲位置放入冲突元素

虽然好想好放,但是因此带来的更大问题,难找更难删,所以一般不采用

2.开散列(链地址法):出现冲突,让冲突的位置变成一个链表

就这样做解决了一部分问题,但随着数据的不断增加,哈希冲突的概率不断增大,在当前数组中,某些链表的长度会很长,查找效率依然下降

解决方案:

1.整个数组扩容,对数组内元素进行搬移,原先冲突的元素大概率不会在冲突

2.将长度过于长的链表转为BST

5.负载因子 

3.基于开散列方案的HashMap实现

 1.HashMap类中的属性

   private static class Node{
        int key;
        int value;
        Node next;

        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    private int size;

    private int M;

    private Node[] data;

    //负载因子
    private static final double LOAD_FACTOR = 0.75;

    public MyHashMap(int capacity) {
        this.data = new Node[capacity];
        this.M = capacity;
    }

    public MyHashMap() {
        this(16);
    }

2.哈希函数

    //哈希函数
    private int hash(int key) {
        return key % this.M;
    }

3.判断当前哈希表中是否含有指定的key值

    //判断当前哈希表中是否含有指定的key值
    public boolean containsKey(int key){
        int index = hash(key);
        for (Node i = data[index]; i != null ; i = i.next) {
            if(i.key == key) {
                return true;
            }
        }
        return false;
    }

4.判断当前哈希表中是否包含指定的value值


    //判断当前哈希表中是否包含指定的value值
    public boolean containsValue(int value) {
        for(int i = 0; i < data.length; i++) {
            for(Node x = data[i]; x != null; x = x.next) {
                if(x.value == value) {
                    return true;
                }
            }
        }
        return false;
    }

5.在哈希表中新增,修改元素

    //在哈希表中新增,修改元素
    public int put(int key, int value) {
        int index = hash(key);
        //判断是否存在
        for(Node x = data[index]; x != null; x = x.next) {
            if(x.key == key) {
                int oldValue = x.value;
                x.value = value;
                return oldValue;
            }
        }
        //此时一定不存在
        Node node = new Node(key,value);
        node.next = data[index];
        data[index] = node;
        size++;
        //判断是否需要扩容
        if(size >= data.length * LOAD_FACTOR) {
            resize();
        }
        return -1;
    }

6.哈希表扩容

    private void resize() {
        this.M = data.length << 1;
        Node [] newData = new Node[M];
        for (int i = 0; i < data.length; i++) {
            for (Node x = data[i]; x != null;) {
                Node next = x.next;
                int newIndex = hash(x.key);
                x.next = newData[newIndex];
                newData[newIndex] = x;
                x = next;
            }
        }
        data = newData;
    }

7.删除哈希表中指定的key节点


    //删除哈希表中指定的key节点
    public boolean removeKey(int key) {
        int index = hash(key);
        //判空
        if(data[index] == null) {
            return false;
        }
        //判断是否为头节点
        if(data[index].key == key) {
            data[index] = data[index].next;
            size--;
            return true;
        }
        Node prev = data[index];
        while (prev.next != null) {
            if(prev.next.key == key) {
                prev.next = prev.next.next;
                size--;
                return true;
            }
        }
        return false;
    }

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值