链式哈希表

6 阅读2分钟

代码

#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;

// 链式哈希表
class HashTable {
public:
  HashTable(int size = primes_[0], double loadFactor=0.75) 
    : useBucketNum_(0)
    , loadFactor_(loadFactor)
    , primeIdx_(0)  {
      if (size != primes_[0]) {  // 用户传入的值和初始化值不一样,找到一个素数,该素数大于用户传入值
        for (; primeIdx_ < PRIME_SIZE; primeIdx_++) {
          if (primes_[primeIdx_] >= size) {
            break;
          }
        }

        // 素数表遍历完了还是没找到大于用户传入值的素数,使用最后一个素数
        if (primeIdx_ == PRIME_SIZE) {
          primeIdx_--;
        }
      }

      table_.resize(primes_[primeIdx_]);

  }

public:
  // 增加元素
  // 实现的是一个不能重复插入的函数
  void insert(int key) {
    // 判断扩容
    double factor = useBucketNum_ * 1.0 / table_.size();

    std::cout << "factor: " << factor << std::endl;

    // 需要扩容
    if (factor > loadFactor_) {
      expand();
    }

    // 计算一下放到哪个桶中
    int idx = key % table_.size();

    if (table_[idx].empty()) {
      // 桶是空的, 第一次使用这个桶
      useBucketNum_++;
      table_[idx].emplace_front(key);
    } else {
      // 不是第一次使用这个桶,需要在链表中查找下是否已经有这个元素了
      auto it = ::find(table_[idx].begin(), table_[idx].end(), key);

      if (it == table_[idx].end()) {
        // 没有找到,直接可以插入key
        table_[idx].emplace_front(key);
      }
    }

  }

  // 删除元素
  void erase(int key) {
    int idx = key % table_.size(); // 计算桶的下标
    auto it = ::find(table_[idx].begin(), table_[idx].end(), key);

    if (it != table_[idx].end()) {
      // 找到了,删除
      table_[idx].erase(it);

      // 删除完以后需要判断下这个桶是不是空了
      if (table_[idx].empty()) {
        useBucketNum_--;
      }
    }
  }

  // 搜索元素
  bool find(int key) {
    int idx = key % table_.size(); // 计算桶的下标
    auto it = ::find(table_[idx].begin(), table_[idx].end(), key);

    if (it != table_[idx].end()) {
      // 找到了
      return true;
    }

    return false;
  }

private:
  // 扩容函数
  void expand() {
    if (primeIdx_ + 1 == PRIME_SIZE) {
      // 已经使用了最后一个素数,无法扩容了
      throw "hashtable can not expand anymore";
    }

    primeIdx_++;
    useBucketNum_ = 0;

    vector<list<int>> oldTable;
    table_.swap(oldTable);

    table_.resize(primes_[primeIdx_]);  // 重新调整长度

    for (auto list : oldTable) {
      for (auto key : list) {
        // 计算一下放到哪个桶中
        int idx = key % table_.size();

        if (table_[idx].empty()) {
          // 桶是空的, 第一次使用这个桶
          useBucketNum_++;
        }
        table_[idx].emplace_front(key);
      }
    }

  }

private:
  vector<list<int>> table_;  // 哈希表的数据结构
  int useBucketNum_;   // 已使用桶的个数
  double loadFactor_;  // 记录哈希表装载因子的

  static const int PRIME_SIZE  = 10;  // 素数表的大小
  static int primes_[PRIME_SIZE];  // 素数表 
  int primeIdx_;   // 当前使用的素数的下标

};

// 素数表初始化
int HashTable::primes_[PRIME_SIZE] = {3, 7, 23, 47, 97, 251, 443, 911, 1471, 42773};

测试

int main() {
  HashTable htable;

  // 插入数据
  htable.insert(21);
  htable.insert(32);
  htable.insert(14);
  htable.insert(15);
  htable.insert(19);
  htable.insert(78);
  htable.insert(678);

  // 查找
  std::cout << "查找结果:" << htable.find(14) << std::endl;

  // 删除
  htable.erase(14);
  std::cout << "查找结果:" << htable.find(14) << std::endl;

  return 0;
}
➜  build git:(main) ✗ ./HashTable    
factor: 0
factor: 0.333333
factor: 0.666667
factor: 0.666667
factor: 0.666667
factor: 1
factor: 0.571429
查找结果:1
查找结果:0