哈希表原理及简单设计
Posted 却把清梅嗅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了哈希表原理及简单设计相关的知识,希望对你有一定的参考价值。
本文为博主算法学习过程中的学习笔记,主要内容来源于其他平台或书籍,出处请参考下方 参考&感谢 一节。
介绍
哈希表 是一种使用哈希函数组织数据,以支持快速插入和搜索的数据结构。
有两种不同类型的哈希表:哈希集合 和 哈希映射。
- 哈希集合是 集合 数据结构的实现之一,用于存储 非重复值。
- 哈希映射是 映射 数据结构的实现之一,用于存储
(key, value)
键值对。
通过选择合适的哈希函数,哈希表可以在插入和搜索方面实现出色的性能。
原理及设计关键
哈希表的关键思想是使用哈希函数 将键映射到存储桶。更确切地说,
- 1、当我们插入一个新的键时,哈希函数将决定该键应该分配到哪个桶中,并将该键存储在相应的桶中;
- 2、当我们想要搜索一个键时,哈希表将使用相同的哈希函数来查找对应的桶,并只在特定的桶中进行搜索。
1、哈希函数
哈希函数是哈希表中最重要的组件,该哈希表用于将键映射到特定的桶。
散列函数将取决于 键值的范围 和 桶的数量。
哈希函数的设计是一个开放的问题。其思想是尽可能将键分配到桶中,理想情况下,完美的哈希函数将是键和桶之间的一对一映射。然而,在大多数情况下,哈希函数并不完美,它需要在桶的数量和桶的容量之间进行权衡。
2、解决冲突
理想情况下,如果我们的哈希函数是完美的一对一映射,我们将不需要处理冲突。不幸的是,在大多数情况下,冲突几乎是不可避免的。
冲突解决算法应该解决以下几个问题:
1、如何组织在同一个桶中的值?
2、如果为同一个桶分配了太多的值,该怎么办?
3、如何在特定的桶中搜索目标值?
根据我们的哈希函数,这些问题与 桶的容量 和可能映射到 同一个桶的键的数目 有关。
让我们假设存储最大键数的桶有 N
个键。
通常,如果 N
是常数且很小,我们可以简单地使用一个数组将键存储在同一个桶中。如果 N 是可变的或很大,我们可能需要使用 高度平衡的二叉树 来代替。
设计哈希集合
不使用任何内建的哈希表库设计一个哈希集合。
在本文中,我们使用单独链接法,来看看它是如何工作的。
- 1、从本质上讲,
HashSet
的存储空间相当于连续内存数组,这个数组中的每个元素相当于一个桶。 - 2、给定一个值,我们首先通过哈希函数生成对应的散列值来定位桶的位置。
- 3、一旦找到桶的位置,则在该桶上做相对应的操作,如
add,remove,contains
。
对于桶的设计,我们有几种选择,可以使用数组来存储桶的所有值。然而数组的一个缺点是需要O(N)
的时间复杂度进行插入和删除,而不是O(1)
。
因为任何的更新操作,我们首先是需要扫描整个桶为了避免重复。选择链表来存储桶的所有值是更好的选择,插入和删除具有常数的时间复杂度。
public class MyHashSet
private int keyRange;
private Bucket[] buckets;
/**
* Initialize your data structure here.
*/
public MyHashSet()
this.keyRange = 793;
this.buckets = new Bucket[this.keyRange];
for (int i = 0; i < keyRange; i++)
buckets[i] = new Bucket();
protected int _hash(int key)
return key % this.keyRange;
public void add(int key)
int index = this._hash(key);
buckets[index].insert(key);
public void remove(int key)
int index = this._hash(key);
buckets[index].delete(key);
public boolean contains(int key)
int index = this._hash(key);
return buckets[index].contain(key);
class Bucket
private LinkedList<Integer> container;
Bucket()
this.container = new LinkedList<>();
void insert(Integer key)
int index = container.indexOf(key);
if (index == -1)
container.addFirst(key);
void delete(Integer key)
container.remove(key);
boolean contain(Integer key)
return container.indexOf(key) != -1;
设计哈希映射
不使用任何内建的哈希表库设计一个哈希映射。
class MyHashMap
private int keyRange;
private Node[] nodes;
public MyHashMap()
this.keyRange = 793;
this.nodes = new Node[this.keyRange];
protected int _hash(int key)
return key % this.keyRange;
public void put(int key, int value)
int index = this._hash(key);
Node curr = nodes[index];
if (curr == null)
Node node = new Node(key, value);
nodes[index] = node;
return;
while (curr != null)
if (curr.key == key)
curr.value = value;
return;
if (curr.next == null)
Node node = new Node(key, value);
node.prev = curr;
node.next = curr.next; // curr.next = null
curr.next = node;
return;
else
curr = curr.next;
public int get(int key)
int index = this._hash(key);
Node curr = nodes[index];
while (curr != null)
if (curr.key == key)
return curr.value;
else
curr = curr.next;
return -1;
public void remove(int key)
int index = this._hash(key);
Node curr = nodes[index];
if (curr != null && curr.key == key)
Node next = curr.next;
if (next != null)
next.prev = null;
nodes[index] = next;
return;
while (curr != null)
if (curr.key == key)
Node prev = curr.prev;
Node next = curr.next;
if (prev != null)
prev.next = next;
if (next != null)
next.prev = prev;
return;
else
curr = curr.next;
class Node
Integer key;
Integer value;
Node next;
Node prev;
Node(Integer key, Integer value)
this.key = key;
this.value = value;
参考 & 感谢
文章绝大部分内容节选自LeetCode
,概述:
- https://leetcode-cn.com/explore/learn/card/hash-table/203/design-a-hash-table/797/
例题:
- https://leetcode-cn.com/problems/design-hashset/
- https://leetcode-cn.com/problems/design-hashmap/
关于我
Hello,我是 却把清梅嗅 ,如果您觉得文章对您有价值,欢迎 ❤️,也欢迎关注我的 博客 或者 GitHub。
如果您觉得文章还差了那么点东西,也请通过关注督促我写出更好的文章——万一哪天我进步了呢?
以上是关于哈希表原理及简单设计的主要内容,如果未能解决你的问题,请参考以下文章