C++语言实现哈希表中的线性探测法和平方探测法

哈希表(Hash表),也称为散列表,是一种数据结构,通过使用哈希函数将键映射到数组的特定位置来实现高效的查找、插入和删除操作。

哈希函数将键转换为一个整数,这个整数对应数组中的索引。当要查找一个键时,哈希函数会将键转换为对应的索引,然后在数组中查找该索引处是否有对应的值。这个过程非常快速,通常只需要常数时间,因此哈希表的查找操作非常高效。

然而,哈希表的性能也受到哈希函数的质量影响。一个好的哈希函数应该尽可能地将键映射到不同的索引上,以减少哈希冲突(即多个键映射到同一个索引上的情况),从而保证哈希表的高效性能。

哈希表冲突(即多个键映射到同一个索引上的情况)是无法避免的,解决哈希冲突的方法有很多种,其中包括线性探测法和平方探测法。

线性探测法是一种简单的哈希冲突解决方法,当发生哈希冲突时,线性探测法会在哈希表中从冲突的位置开始,依次往后查找下一个空槽,并将键值对插入到该位置上。这样,当查找一个键时,如果该键在哈希表中存在,就可以在不停止搜索的情况下遍历哈希表,直到找到键值对或者确定该键值对不存在为止。线性探测法的缺点是它容易出现“聚集”(clustering)现象,即多个键值对映射到相邻的位置上,导致查找性能下降。

平方探测法是一种改进的哈希冲突解决方法,它解决了线性探测法的“聚集”现象。当发生哈希冲突时,平方探测法会从冲突的位置开始,依次检查索引位置 i + 1, i + 4, i + 9, i + 16, … 直到找到一个空槽并将键值对插入到该位置上。这种探测方法可以更加均匀地分布键值对,减少“聚集”现象的发生。平方探测法的缺点是它可能会出现“探测不到”的情况,即当哈希表中的空槽不足时,键值对可能无法插入到哈希表中,导致查找操作失败。

选择哪种哈希冲突解决方法取决于应用场景和哈希表的负载因子。如果哈希表的负载因子较低,即哈希表中的键值对比较少,线性探测法可能是一种更好的选择。如果哈希表的负载因子较高,即哈希表中的键值对比较多,平方探测法可能是一种更好的选择。

以下是使用C++实现线性探测法的示例代码

// 哈希表中的线性探测法
class LinearProbingHashTable {
private:
    static const int DEFAULT_CAPACITY = 10;
    vector<pair<int, int>> table; // 哈希表
    int size; // 哈希表中的键值对数目

public:
    LinearProbingHashTable() : table(DEFAULT_CAPACITY), size(0) {}

    void put(int key, int value) {
        int index = hash(key);
        while (table[index].first != 0 && table[index].first != key) {
            index = (index + 1) % table.size();
        }
        if (table[index].first == 0) {
            size++;
        }
        table[index] = {key, value};
    }

    int get(int key) {
        int index = hash(key);
        while (table[index].first != 0) {
            if (table[index].first == key) {
                return table[index].second;
            }
            index = (index + 1) % table.size();
        }
        return -1; // 没有找到键对应的值
    }

private:
    int hash(int key) {
        return key % table.size();
    }
};


// 哈希表中的平方探测法
class QuadraticProbingHashTable {
private:
    static const int DEFAULT_CAPACITY = 10;
    vector<pair<int, int>> table; // 哈希表
    int size; // 哈希表中的键值对数目

public:
    QuadraticProbingHashTable() : table(DEFAULT_CAPACITY), size(0) {}

    void put(int key, int value) {
        int index = hash(key);
        int offset = 1;
        while (table[index].first != 0 && table[index].first != key) {
            index = (index + offset * offset) % table.size();
            offset++;
        }
        if (table[index].first == 0) {
            size++;
        }
        table[index] = {key, value};
    }

    int get(int key) {
        int index = hash(key);
        int offset = 1;
        while (table[index].first != 0) {
            if (table[index].first == key) {
                return table[index].second;
            }
            index = (index + offset * offset) % table.size();
            offset++;
        }
        return -1; // 没有找到键对应的值
    }

private:
    int hash(int key) {
        return key % table.size();
    }
};

以上代码中,LinearProbingHashTable 类实现了哈希表中的线性探测法,QuadraticProbingHashTable 类实现了哈希表中的平方探测法。在 put 方法中,如果哈希表中已经存在该键,则更新该键对应的值;如果哈希表中不存在该键,则在哈希表中查找下一个空槽,并将该键值对插入到该位置上。在 get 方法中,根据键值计算哈希值,并从哈希表中查找对应的值。如果查找过程中发现空槽,则该键值对不存在于哈希表中,返回 -1 表示未找到。在哈希表中,hash 函数用于计算键值对应的哈希值,这里使用了简单的取模操作来实现。table 是一个动态数组,存储哈希表中的键值对。size 是哈希表中键值对的数目,用于计算哈希表的负载因子。当哈希表中的键值对数目超过了哈希表的容量时,需要进行扩容操作,这里为了简化代码,没有实现这部分功能。

以下是使用C++实现平方探测法的示例代码:

#include <iostream>
#include <vector>

using namespace std;

class HashTable {
private:
    int size;  // 哈希表大小
    vector<int> table;  // 存储哈希表中的元素

public:
    HashTable(int size): size(size) {
        table.resize(size, -1);  // 初始化哈希表,全部置为-1
    }

    // 哈希函数,将key映射到哈希表的位置
    int hash(int key) {
        return key % size;
    }

    // 插入元素
    void insert(int key) {
        int index = hash(key);  // 计算key的哈希值

        // 如果哈希表中该位置已经有元素了,进行平方探测
        int i = 1;
        while (table[index] != -1) {
            index = (index + i * i) % size;
            i++;
        }

        // 找到一个空位置,插入元素
        table[index] = key;
    }

    // 查找元素
    bool find(int key) {
        int index = hash(key);  // 计算key的哈希值

        // 如果哈希表中该位置不是要查找的元素,进行平方探测
        int i = 1;
        while (table[index] != key && table[index] != -1) {
            index = (index + i * i) % size;
            i++;
        }

        // 找到要查找的元素
        if (table[index] == key) {
            return true;
        }

        // 没有找到要查找的元素
        return false;
    }

    // 删除元素
    void remove(int key) {
        int index = hash(key);  // 计算key的哈希值

        // 如果哈希表中该位置不是要删除的元素,进行平方探测
        int i = 1;
        while (table[index] != key && table[index] != -1) {
            index = (index + i * i) % size;
            i++;
        }

        // 找到要删除的元素
        if (table[index] == key) {
            table[index] = -1;
        }
    }
};

int main() {
    HashTable hashTable(7);

    hashTable.insert(10);
    hashTable.insert(22);
    hashTable.insert(37);

    cout << hashTable.find(22) << endl;  // 输出1
    cout << hashTable.find(33) << endl;  // 输出0

    hashTable.remove(22);

    cout << hashTable.find(22) << endl;  // 输出0

    return 0;
}

在这个示例代码中,我定义了一个名为HashTable的哈希表类,它包含一个私有成员size表示哈希表大小,和一个存储哈希表中元素的vector。实现了三个公共成员函数:

  • hash函数:将关键字映射到哈希表中的位置。
  • insert函数:向哈希表中插入元素。如果插入时发生冲突,则使用平方探测找到下一个可用位置。
  • find函数:在哈希表中查找元素。如果查找时发生冲突,则使用平方探测找到下一个可能包含该元素的位置。
  • remove函数:从哈希表中删除元素。如果要删除的元素不在哈希表中,则使用平方探测找到下一个可能包含该元素的位置。

insertfindremove函数中,我们使用了平方探测来解决哈希表中的冲突。从计算哈希值的位置开始,不断计算该位置的平方,直到找到一个空位置或者找到了要插入/查找/删除的元素。如果哈希表已满并且没有找到空位置,则说明哈希表已经被占满了,需要重新调整哈希表的大小或者采用其他的哈希表实现方式。

以上是一个简单的平方探测法的实现示例。实际上,平方探测法还存在一些问题,如二次探测序列中的元素可能会聚集在一起,导致哈希表效率下降。因此,实际应用中需要针对具体问题选择合适的哈希表实现方式。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值