【数据结构】哈希桶

本文介绍了解决哈希冲突的一种方法——开散列法,并详细展示了使用链地址法实现哈希表的具体过程。文章包括哈希表的创建、扩容、查找、插入和删除操作的实现。

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

  在上一篇博客中,我们简单地介绍了哈希表,以及解决哈希冲突的办法;今天我们介绍解决哈希冲突的另一种方法——开散列法。开散列法又叫链地址法,首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点组成一个向量。说起来可能很复杂,其实很简单,看一个图就明白了:假设哈希函数为Hash(key)=key%10
这里写图片描述

  • 假如哈希函数被破解了,可能会对哈希桶进行攻击,从而将哈希桶退化成单链表,此时查找效率将会大大降低,我们可以把单链表换成红黑树,提高查找效率。
  • 当哈希桶的个数与插入的结点个数相等时,我们就增加容量。

以下是我实现的哈希桶:

// 获得一个适合的素数
const int _PrimeSize = 28;//素数表
static const unsigned long _PrimeList[_PrimeSize] =
{
    53ul, 97ul, 193ul, 389ul, 769ul,
    1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
    49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
    1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
    50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
    1610612741ul, 3221225473ul, 4294967291ul
};
size_t GetNextPrime(size_t num)
{
    for (size_t i = 0; i < _PrimeSize; i++)
    {
        if (_PrimeList[i]>num)
            return _PrimeList[i];
    }
    return _PrimeList[_PrimeSize - 1];
}

template <class K>
class KeyToIntDef
{
public:
    size_t operator()(const K& key)
    {
        return key;
    }
};
// 将字符串转换成整型
static size_t BKDRHash(const char * str)
{
    unsigned int seed = 131; // 31 131 1313 13131 131313
    unsigned int hash = 0;
    while (*str)
    {
        hash = hash * seed + (*str++);
    }
    size_t ret = (hash & 0x7FFFFFFF);
    return ret;
}
class StringToInt
{
public:
    size_t operator()(const string& key)
    {
        return BKDRHash(key.c_str());
    }
};


#include <iostream>
#include <vector>
#include <string>
using namespace std;
template <class K, class V>
struct HashNode//结点
{
    pair<K, V> _kv;
    HashNode<K, V>* _pNext;
    HashNode(const pair<K, V>& kv)
        : _kv(kv)
        , _pNext(NULL)
    {}
};

template <class K, class V, class KeyToInt>
class HashTable;//前置声明
//迭代器
template <class K, class V, class KeyToInt = KeyToIntDef<K>>
struct HashTableIterator
{
    typedef HashNode<K, V> Node;
    typedef Node* PNode;
    typedef HashTableIterator<K, V, KeyToInt> Self;

    PNode _pCur;
    HashTable<K, V, KeyToInt>* _ht;
public:
    HashTableIterator()
        : _pCur(NULL)
        , _ht(NULL)
    {}

    HashTableIterator(const PNode pCur, HashTable<K, V, KeyToInt>* ht)
        : _pCur(pCur)
        , _ht(ht)
    {}

    HashTableIterator(Self& s)
        : _pCur(s._pCur)
        , _ht(s._ht)
    {}

    Self& operator++()
    {
        Next();
        return *this;
    }

    Self operator++(int)
    {
        HashTableIterator temp(*this);
        Next();
        return temp;
    }

    pair<K, V>& operator*()
    {
        return _pCur->_kv;
    }

    pair<K, V>* operator->()
    {
        return &(operator*());
    }

    bool operator==(const Self& s)
    {
        return _pCur == s._pCur;
    }

    bool operator!=(const Self& s)
    {
        return _pCur != s._pCur;
    }
private:
    void Next()
    {
        if (_pCur->_pNext)
            _pCur = _pCur->_pNext;
        else
        {//找下一个非空桶
            size_t bucketNo = _ht->HashFunc(_pCur->_kv.first) + 1;
            for (; bucketNo < _ht->_hashTable.capacity(); bucketNo++)
            {
                if (_ht->_hashTable[bucketNo])
                {
                    _pCur = _ht->_hashTable[bucketNo];
                    return;
                }
            }
            _pCur = NULL;//没有找到
        }
        return;
    }
};
//哈希桶
template <class K, class V, class KeyToInt = KeyToIntDef<K>>
class HashTable
{
    typedef HashNode<K, V> Node;
    typedef Node* PNode;
    friend HashTableIterator<K, V, KeyToInt>;
public:
    typedef HashTableIterator<K, V, KeyToInt> Iterator;
public:
    HashTable(size_t capacity = 10)
    {
        capacity = GetNextPrimer(capacity);
        _hashTable.resize(capacity);
        _size = 0;
    }

    Iterator Begin()
    {
        for (size_t bucketNo = 0; bucketNo < _hashTable.capacity(); bucketNo++)
        {
            if (_hashTable[bucketNo])
                return Iterator(_hashTable[bucketNo], this);
        }
        return Iterator(NULL, this);
    }

    Iterator End()
    {
        return Iterator(NULL, this);
    }
//////////////////////////插入,查找及删除/////////////////////////////////
    pair<Iterator, bool> InsertEqual(const pair<K, V>& kv)//允许重复
    {
        CheckCapacity();
        size_t bucketNo = HashFunc(kv.first);
        PNode pNewNode = new Node(kv);
        pNewNode->_pNext = _hashTable[bucketNo];//头插
        _hashTable[bucketNo] = pNewNode;
        _size++;
        return make_pair(Iterator(pNewNode, this), true);
    }

    pair<Iterator, bool> InsertUnique(const pair<K, V>& kv)//key值唯一
    {
        CheckCapacity();
        size_t bucketNo = HashFunc(kv.first);
        PNode pCur = _hashTable[bucketNo];
        while (pCur)
        {
            if (kv.first == pCur->_kv.first)
                return make_pair(Iterator(pCur, this), false);
            pCur = pCur->_pNext;
        }
        pCur = new Node(kv);
        pCur->_pNext = _hashTable[bucketNo];
        _hashTable[bucketNo] = pCur;
        _size++;
        return make_pair(Iterator(pCur, this), true);
    }

    Iterator Find(const K& key)
    {
        size_t bucketNo = HashFunc(key);
        PNode pCur = _hashTable[bucketNo];

        while (pCur)
        {
            if (pCur->_kv.first == key)
                return Iterator(pCur, this);
            pCur = pCur->_pNext;
        }
        return Iterator(NULL, this);
    }

    Iterator Erase(Iterator pos)//key值唯一
    {
        if (pos._pCur == NULL)
            return Iterator(NULL, this);
        size_t key = pos._pCur->_kv.first;
        size_t bucketNo = HashFunc(key);
        PNode pCur = _hashTable[bucketNo];
        PNode pPre = NULL;
        while (pCur)
        {
            if (pCur->_kv.first == key)
            {
                if (_hashTable[bucketNo] == pCur)//pCur是首元素结点
                    _hashTable[bucketNo] = pCur->_pNext;
                else
                    pPre->_pNext = pCur->_pNext;
                delete pCur;
                pCur = NULL;
                _size--;
                return Iterator(pPre, this);
            }
            else
            {
                pPre = pCur;
                pCur = pCur->_pNext;
            }
        }
        return Iterator(NULL, this);
    }

    size_t Erase(const K& key)//key值可以重复
    {
        size_t oldsize = _size;
        size_t bucketNo = HashFunc(key);
        PNode pCur = _hashTable[bucketNo];
        PNode pPre = NULL;
        while (pCur)
        {
            if (pCur->_kv.first == key)
            {
                if (pCur == _hashTable[bucketNo])
                {
                    _hashTable[bucketNo] = pCur->_pNext;
                    delete pCur;
                    pCur = _hashTable[bucketNo];
                }
                else
                {
                    pPre->_pNext = pCur->_pNext;
                    delete pCur;
                    pCur = pPre->_pNext;
                }
                _size--;
            }
            else
            {
                pPre = pCur;
                pCur = pPre->_pNext;
            }
        }
        if (oldsize == _size)
            return 0;
        else
            return oldsize - _size;
    }
///////////////////////////其它常用函数///////////////////////////////
    size_t Count(const K key)//值为key的元素个数
    {
        size_t count = 0;
        size_t bucketNo = HashFunc(key);
        PNode pCur = _hashTable[bucketNo];
        while (pCur)
        {
            if (pCur->_kv.first == key)
                count++;
            pCur = pCur->_pNext;
        }
        return count;
    }

    size_t BucketCount()const//桶的个数
    {
        return _hashTable.capacity();
    }

    size_t BucketSize(size_t bucketNo)const//桶内元素个数
    {
        size_t count = 0;
        PNode pCur = _hashTable[bucketNo];
        while (pCur)
        {
            count++;
            pCur = pCur->_pNext;
        }
        return count;
    }

    V& FindORInsert(const K& key)//查找值为key,如果找到了,返回对应的value,
                                 //如果没有找到插入该结点,返回缺省的value
    {
        Iterator ret = InsertUnique(make_pair(key, V())).first;
        return (*ret).second;
    }

    bool Empty()const//是否为空
    {
        return _size == 0;
    }

    size_t Size()const//插入的总数
    {
        return _size;
    }

    void Clear()//清空
    {
        for (size_t bucketNo = 0; bucketNo < _hashTable.capacity(); bucketNo++)
        {
            PNode pCur = _hashTable[bucketNo];
            while (pCur)
            {
                _hashTable[bucketNo] = pCur->_pNext;
                delete pCur;
                pCur = _hashTable[bucketNo];
                _size--;
            }
        }
    }

    ~HashTable()
    {
        Clear();
    }
private:
    size_t HashFunc(const K& key)//哈希函数
    {
        return KeyToInt()(key) % _hashTable.capacity();
    }

    void CheckCapacity()//扩容
    {
        size_t capacity = _hashTable.capacity();
        if (_size == capacity)
        {
            HashTable<K, V, KeyToInt> newHt(GetNextPrime(capacity));
            for (size_t bucketNo = 0; bucketNo < capacity; bucketNo++)
            {
                PNode pCur = _hashTable[bucketNo];
                while (pCur)
                {
                    newHt.InsertEqual(pCur->_kv);
                    pCur = pCur->_pNext;
                }
            }
            _hashTable.swap(newHt._hashTable);
        }
    }
private:
    vector<PNode> _hashTable;
    size_t _size;
};

void test()
{
    HashTable<int, int> ht;
    ht.InsertEqual(make_pair(1, 1));
    ht.InsertEqual(make_pair(5, 5));
    ht.InsertEqual(make_pair(15, 15));
    ht.InsertEqual(make_pair(15, 15));
    ht.InsertEqual(make_pair(35, 35));
    ht.InsertEqual(make_pair(9, 9));
    HashTable<int, int>::Iterator it = ht.Begin();
    if (!ht.Empty())
    {
        while (it != ht.End())
        {
            cout << it->first << " " ;
            cout << (*it).second << endl;
            it++;
        }
        cout << endl;
        cout << ht.BucketSize(5) << endl;
        cout << ht.BucketCount() << endl;
        cout << ht.Count(15) << endl;
    }
    it = ht.Begin();
    cout << ht.Erase(15) << endl;
    HashTable<int, int>::Iterator ret = ht.Find(1);
    ht.Erase(ret);
    cout << ht.Size() << endl;
    ht.Clear();

    HashTable<string, string, StringToInt> ht1;
    ht1.InsertUnique(make_pair("111", "111"));
    ht1.InsertUnique(make_pair("111", "111"));
    cout << ht1.FindORInsert("111") << endl;
}
### 哈希数据结构的概念 哈希是一种通过特定算法将输入映射到固定长度输出的数据结构技术。其核心在于利用哈希函数将键值转换为存储地址,从而实现高效的插入、删除和查找操作[^1]。 #### 哈希表的核心特性 哈希表是一种基于数组构建的高效数据结构,在实际应用中广泛用于解决快速查找问题。它的速度优势使其成为许多场景下的首选工具,例如 Redis 的底层设计采用了大规模哈希表来支持高性能读写操作[^1]。此外,数据库中的索引机制也可能采用哈希索引来加速查询过程,而操作系统中的注册表同样依赖于哈希表提供快速访问能力。 --- ### 哈希冲突及其解决方案 尽管理想情况下每个键都能被唯一映射至一个桶位,但在现实世界中由于有限的空间以及可能存在的不同键产生相同散列值的情况(即 **哈希冲突**),因此需要引入有效的策略应对这种情况: 1. **拉链法 (Separate Chaining)** 当发生冲突时,可以将具有相同哈希值的所有元素存入同一个链表或者更复杂的动态集合中。这种方法简单易懂且易于扩展容量大小以适应更多条目加入需求[^2]。 2. **开放寻址法 (Open Addressing)** 开放寻址方法是在遇到冲突之后寻找其他可用槽位继续尝试放置当前项直到成功为止的一种方式。具体又分为几种子类别如线性探测(linear probing),二次方程探测(quadratic probing) 和双重哈希(double hashing)[^3]。 以下是双倍哈希的具体公式表示形式: ```python (position = (hash1(key) + i * hash2(key)) % table_size) ``` 这里 `i` 表示第几次试探失败后的增量系数;`hash1()` 是主要负责定位初始候选位置的基础哈希运算器;而辅助性的 `hash2()` 则用来决定每次跳跃距离以便避开那些已被占据的位置群组[^3]。 --- ### 简单的手动实现哈希表 下面展示了一个非常基础版本的 Python 中自定义哈希表类实例化流程图解说明如下所示: ```python class HashTable: def __init__(self, size=10): self.size = size self.table = [[] for _ in range(size)] def _hash_function(self, key): return abs(hash(key)) % self.size def insert(self, key, value): index = self._hash_function(key) bucket = self.table[index] found = False for idx, record in enumerate(bucket): k, v = record if k == key: bucket[idx] = (key, value) # 更新已有记录 found = True break if not found: bucket.append((key, value)) def search(self, key): index = self._hash_function(key) bucket = self.table[index] for k, v in bucket: if k == key: return v raise KeyError(f"Key {key} does not exist.") def delete(self, key): index = self._hash_function(key) bucket = self.table[index] for idx, record in enumerate(bucket): k, v = record if k == key: del bucket[idx] return f"Deleted Key: {k}" raise KeyError(f"Key {key} does not exist.") ``` 此代码片段展示了如何创建一个简单的哈希表,并提供了基本功能包括插入(`insert`)、检索(`search`) 及移除 (`delete`) 操作的支持[^1]. --- ### 应用领域 - **缓存系统**: 使用 LRU 缓存配合哈希表可显著提升热点资源加载效率。 - **分布式环境**: 如一致性哈希算法帮助节点间均匀分配负载的同时保持高容错率。 - **密码学安全验证**: SHA系列消息摘要算法便是典型代表之一,它们通过对原始文件计算独一无二指纹串确保传输过程中未遭篡改破坏等问题存在可能性极低程度下完成身份认证手续办理工作等等诸多方面均有所体现出来效果良好表现优异等特点值得我们深入研究探讨学习借鉴吸收采纳运用推广开来形成规模效应最大化发挥各自特长共同进步成长壮大起来吧! ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值