友情支持

如果您觉得这个笔记对您有所帮助,看在D瓜哥码这么多字的辛苦上,请友情支持一下,D瓜哥感激不尽,😜

支付宝

微信

有些打赏的朋友希望可以加个好友,欢迎关注D 瓜哥的微信公众号,这样就可以通过公众号的回复直接给我发信息。

wx jikerizhi

公众号的微信号是: jikerizhi因为众所周知的原因,有时图片加载不出来。 如果图片加载不出来可以直接通过搜索微信号来查找我的公众号。

705. 设计哈希集合

不使用任何内建的哈希表库设计一个哈希集合(HashSet)。

实现 MyHashSet 类:

  • void add(key) 向哈希集合中插入值 key

  • bool contains(key) 返回哈希集合中是否存在这个值 key

  • void remove(key) 将给定值 key 从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。

示例:

输入:
["MyHashSet", "add", "add", "contains", "contains", "add", "contains", "remove", "contains"]
[[], [1], [2], [1], [3], [2], [2], [2], [2]]
输出:
[null, null, null, true, false, null, true, null, false]

解释:
MyHashSet myHashSet = new MyHashSet();
myHashSet.add(1);      // set = [1]
myHashSet.add(2);      // set = [1, 2]
myHashSet.contains(1); // 返回 True
myHashSet.contains(3); // 返回 False ,(未找到)
myHashSet.add(2);      // set = [1, 2]
myHashSet.contains(2); // 返回 True
myHashSet.remove(2);   // set = [1]
myHashSet.contains(2); // 返回 False ,(已移除)

提示:

  • 0 <= key <= 106

  • 最多调用 104addremovecontains

思路分析

三种解决冲突的办法:

  1. 拉链法

  2. 开放地址法

  3. 再哈希法

0705 01
  • 一刷

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
/**
 * @author D瓜哥 · https://www.diguage.com
 * @since 2025-05-10 19:50:48
 */
class MyHashSet {
  private int[] data;
  private int mask;
  private int size;

  public MyHashSet() {
    data = new int[8];
    Arrays.fill(data, -1);
    mask = 8 - 1;
    size = 0;
  }

  public void add(int key) {
    if (size >= data.length * 3 / 4) {
      resize(data.length * 2);
    }
    boolean result = add(data, key, mask);
    if (result) {
      size++;
    }
  }

  public void remove(int key) {
    int index = key & mask;
    boolean ok = false;
    for (int i = index; i < data.length; i++) {
      if (data[i] == key) {
        data[i] = -1;
        ok = true;
      }
    }
    if (!ok) {
      for (int i = 0; i < index; i++) {
        if (data[i] == key) {
          data[i] = -1;
          ok = true;
        }
      }
    }
    if (ok) {
      size--;
    }
    // 需要考虑在初始化时,没有值的情况的缩容问题
    if (size <= data.length / 3 && data.length > 8) {
      resize(data.length / 2);
    }
  }

  public boolean contains(int key) {
    int index = key & mask;
    for (int i = index; i < data.length; i++) {
      if (data[i] == key) {
        return true;
      }
    }
    for (int i = 0; i < index; i++) {
      if (data[i] == key) {
        return true;
      }
    }
    return false;
  }

  private void resize(int length) {
    int[] newData = new int[length];
    Arrays.fill(newData, -1);
    int newMask = length - 1;
    for (int k : data) {
      if (k == -1) {
        continue;
      }
      add(newData, k, newMask);
    }
    this.data = newData;
    this.mask = newMask;
  }

  private boolean add(int[] data, int key, int newMask) {
    int index = key & newMask;
    if (data[index] == -1) {
      data[index] = key;
      return true;
    } else if (data[index] == key) {
      return false;
    } else {
      // 使用开放地址法解决哈希冲突
      for (int j = index + 1; j < data.length; j++) {
        if (data[j] == -1) {
          data[j] = key;
          return true;
        } else if (data[j] == key) {
          return false;
        }
      }
      for (int j = 0; j < index; j++) {
        if (data[j] == -1) {
          data[j] = key;
          return true;
        } else if (data[j] == key) {
          return false;
        }
      }
    }
    return false;
  }
}