使用“开放地址法”实现hashmap

本文探讨了使用开放地址法解决哈希冲突的方法,详细介绍了如何通过线性探测实现哈希表的插入、查找和删除操作。文章对比了拉链法和开放地址法的区别,提出了一种更简洁高效的代码实现方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前的博文中提到了使用拉链法来实现hashmap,本以为掌握了手撕“拉链法”的哈希表已经不错了,没想到今天遇到了要求:使用开放地址法实现哈希表。虽然说最后还是写出来了,但是当时写的代码实在是“又臭又长”,很多地方不够精简,虽然也许能够实现相同的功能,但是可读取比较低,因为完全可以使用更加优雅,更加简练的代码完成。因此重新思考了一下写下此博客,作为一个总结。

首先本题要求的开放地址很简单,就是如果出现了哈希冲突,那就看下一位行不行,如果不行,继续往下,并且本题不要求写出resize和rehash方法。就假设hashmap是定长的,如果超出了长度要求就直接抛出异常。


我之前写的代码存在的问题:

1、因为要求出现哈希冲突就不断向后找,因此我是首先一个while循环,范围是小于hashmap的长度,在这个里面搜索有没有空的,空的直接放入,如果超出长度,再从下标为0的元素开始搜索。因此除了刚才的while循环,还写了一个if -else来判断到底是找到了,还是超出数组下标了,如果超出下标,那么还要再添加一个while用来查找从0位置开始的元素有没有空的。

可以看出来,真的是非常复杂,其实用更简单的逻辑也可以实现相同的功能,那就是取余%。。。。因为一旦超出下标,使用取余数自然就会从0开始重新算,根本不需要之前那么繁琐。

2、删除元素的时候需要考虑一种特殊情况,那就是比如一开始添加key=“你好”的元素,发现此时下标2被占了,往下搜索3为null,因此放在下标为3的位置,后面又将下标为2的元素删除了,然后我们又准备查找key为“你好”的元素,他计算出来的下标为2,那么问题来了,这个key为“你好”的元素此时到底是已经删除了还是没有删除呢?

我当时的想法是在每个元素里面添加一个flag标志,表示这个元素有没有被移动过,也就是添加的时候有没有出现哈希冲突导致重选了地址。但是其实还有更简单的方法。那就是每次删除的时候把这个位置的元素赋值为一个“空对象”,空对象不是指null,而是一个特殊的对象,比如他的key为-1,或者让他的key是null,但是这个元素不是null。

代码如下,写的匆忙,如果有问题可以提出来。

/**
 * 哈希表,解决冲突的方法为:开放地址法,线性探测,探测步长为:1
 */
public class HashTable {

    private DataItem[] hashArray;       //哈希表底层的数组
    private int tableSize;      //哈希表的大小,即为数组容量
    private DataItem nonItem;   //删除的时候自己定义的一个空对象,代表删除标志位,查找的时候会跳过这个标志位

    //构造方法,初始化变量
    public HashTable(int size) {
        tableSize = size;
        hashArray = new DataItem[tableSize];
        nonItem = new DataItem(-1);
    }

    //哈希表的打印方法
    public void displayTable() {
        System.out.print("Table: ");
        for (int i = 0; i < tableSize; i++) {
            if (hashArray[i] != null) {
                System.out.printf(hashArray[i].getData() + " ");
            } else {
                System.out.printf("** ");
            }
        }
        System.out.println();
    }

    //哈希函数:哈希表的灵魂,这里采用取模法
    public int hashFunc(int key) {
        return key % tableSize;
    }

    //插入哈希表的方法
    public void insert(DataItem item) {
        int key = item.getData();
        int hashVal = hashFunc(key);        //对插入哈希表的数据求哈希值

        //当遇到冲突的时候进行线性探测,即哈希值自加一,探测步长为:1
        while (hashArray[hashVal] != null && hashArray[hashVal].getData() != -1) {
            ++hashVal;
            hashVal %= tableSize;   //这里对表大小取模是防止哈希值超过表的大小,也可以判断之后归零
        }

        hashArray[hashVal] = item;   //将数据插入到合适的位置
    }

    //移除操作,传入关键值
    public DataItem delete(int key) {
        int hashVal = hashFunc(key);    //计算关键值的哈希值

        //如果第一次没找到说明发生了冲突,需要线性探测
        while (hashArray[hashVal] != null) {
            if (hashArray[hashVal].getData() == key) {
                DataItem temp = hashArray[hashVal];
                hashArray[hashVal] = nonItem;
                return temp;
            }
            ++hashVal;
            hashVal %= tableSize;
        }
        return null;
    }

    //查找方法,其实插入和删除中都包含查找方法,只不过各有不同
    public DataItem find(int key) {
        int hashVal = hashFunc(key);

        while (hashArray[hashVal] != null) {
            if (hashArray[hashVal].getData() == key) {
                return hashArray[hashVal];
            }
            ++hashVal;
            hashVal %= tableSize;
        }
        return null;
    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值