线性探测哈希表

4 阅读3分钟

线性探测哈希表

代码

#include <cstddef>
#include <iostream>
using namespace std;

// 桶的状态
enum State {
  STATE_UNUSE,  // 从未使用过的桶
  STATE_USING,  // 正在使用的桶
  STATE_DEL,    // 元素被删除了的桶
};

// 桶的类型
struct Bucket {
  Bucket(int key = 0, State state = STATE_UNUSE)
    : key_(key)
    , state_(state) {}

  int key_;   // 存储的数据
  State state_;  // 桶的当前状态
};

// 线性探测哈希表类型
class HashTable {

public:
  // 插入元素
  bool insert(int key) {
    // 是否需要扩容
    double factor = useBucketNum_ * 1.0 / tableSize_;

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

    if (factor > loadFactor_) {
      // 哈希表开始扩容
      expand();
    }

    int idx = key % tableSize_;

    int i = idx;

    do {
      if (table_[i].state_ != STATE_USING) {
        // idx位置的结点没使用,直接插入数据
        table_[i].state_ = STATE_USING;
        table_[i].key_ = key;
        useBucketNum_++;
        return true;
      }

      // idx位置的结点使用了,需要往后遍历找到一个没有使用的结点
      i = (i + 1) % tableSize_;
    } while(i != idx);

    return false;
  }

  // 删除元素
  bool erase(int key) {
    int idx = key % tableSize_;

    int i = idx;
    do {
      if (table_[i].state_ == STATE_USING  && table_[i].key_ == key) {
        table_[i].state_ = STATE_DEL;
        useBucketNum_--;
      }
      i = (i + 1) % tableSize_;
      std::cout << i << std::endl;
    } while (i != idx && table_[i].state_ != STATE_UNUSE);

    return true;
  }

  // 查询
  bool find(int key) {
    int idx = key % tableSize_;
    int i = idx;

    do {
      if (table_[i].state_ == STATE_USING  && table_[i].key_ == key) {
        // 找到了
        return true;
      }
      i = (i + 1) % tableSize_;
    } while (i != idx && table_[i].state_ != STATE_UNUSE);

    // 没找到
    return false;
  }

public:
  HashTable(int size = primes_[0], double loadFactor = 0.75)
      : useBucketNum_(0)
      , loadFactor_(loadFactor)
      , primeIdx_(0) {
        // 用户传入的大小可能不是素数
        // 把用户传入的size调整到最近的比较大的素数上

        if (size != primes_[0]) {
          for (; primeIdx_ < PRIME_SIZE; primeIdx_++) {
            if (primes_[primeIdx_] > size) {
              break;  // 找到一个比用户传入的数字大的素数
            }
          }

          // 用户传入的数值过大,已经超过了最后一个素数,调整到最后一个素数
          if (primeIdx_ == PRIME_SIZE) {
            primeIdx_--;
          }
        }

        tableSize_ = primes_[primeIdx_];

        table_ = new Bucket[tableSize_];

      }

  ~HashTable() {
    delete [] table_;
    table_ = nullptr;
  }
private:
  // 扩容
  void expand() {
    ++primeIdx_;
    if (primeIdx_ == PRIME_SIZE) {
      // 素数都用完了
      throw "HashTable is too large! can not expand";
    }

    Bucket* newTable = new Bucket[primes_[primeIdx_]];

    // rehash
    for (int i = 0; i < tableSize_ ; i++) { 
      if (table_[i].state_ == STATE_USING) {
        int idx = table_[i].key_ % primes_[primeIdx_];

        int k = idx;

        do {
          if (newTable[k].state_ != STATE_USING) {
            // idx位置的结点没使用,直接插入数据
            newTable[k].state_ = STATE_USING;
            newTable[k].key_ = table_[i].key_;
            break;
          }
    
          // idx位置的结点使用了,需要往后遍历找到一个没有使用的结点
          k = (k + 1) % primes_[primeIdx_];
        } while(k != idx);
      }
    }

    // 删除旧的,指向新的
    delete [] table_;
    table_ = newTable;
    tableSize_ = primes_[primeIdx_];
  }

private:
  Bucket* table_;   // 指向动态开辟的哈希表
  int tableSize_;   // 哈希表当前的长度
  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);
}
➜  build git:(main) ✗ ./HashTable    
factor: 0
factor: 0.333333
factor: 0.666667
factor: 1
factor: 0.571429

查找

int main() {
  HashTable htable;

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

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

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

删除


int main() {
  HashTable htable;

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

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

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

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