哈希表

哈希表数据结构以键值对的形式存储元素,其中

  • - 用于索引值的唯一整数
  • - 与键关联的数据。
Hash Table key and data
哈希表中的键和值

哈希(哈希函数)

在哈希表中,使用键处理新索引。然后,将与该键对应的元素存储在该索引中。这个过程称为哈希

k为键,h(x)为哈希函数。

这里,h(k)将为我们提供一个新索引来存储与k关联的元素。

Hash Table representation
哈希表表示

要了解更多信息,请访问哈希


哈希冲突

当哈希函数为多个键生成相同的索引时,将发生冲突(在该索引中存储什么值)。这称为哈希冲突

我们可以使用以下一种技术来解决哈希冲突。

  • 通过链表解决冲突
  • 开放寻址:线性/二次探测和双重哈希

1. 通过链表解决冲突

在链表中,如果哈希函数为多个元素生成相同的索引,则这些元素通过使用双向链表存储在同一索引中。

如果j是多个元素的槽,它包含指向元素列表头部的指针。如果没有元素,j包含NIL

chaining method used to resolve collision in hash table
通过链表解决冲突

操作的伪代码

chainedHashSearch(T, k)
  return T[h(k)]
chainedHashInsert(T, x)
  T[h(x.key)] = x //insert at the head
chainedHashDelete(T, x)
  T[h(x.key)] = NIL

2. 开放寻址

与链表不同,开放寻址不将多个元素存储在同一槽中。在这里,每个槽要么填充有单个键,要么留空NIL

开放寻址中使用的不同技术有

i. 线性探测

在线性探测中,通过检查下一个槽来解决冲突。

h(k, i) = (h′(k) + i) mod m

其中

  • i = {0, 1, ….}
  • h'(k)是一个新的哈希函数

如果h(k, 0)处发生冲突,则检查h(k, 1)。这样,i的值会线性增加。

线性探测的问题在于,一串相邻的槽会被填满。在插入新元素时,必须遍历整个簇。这会增加执行哈希表操作所需的时间。

ii. 二次探测

它的工作原理与线性探测类似,但通过使用以下关系,槽之间的间隔会增加(大于一)。

h(k, i) = (h′(k) + c1i + c2i2) mod m

其中,

  • c1c2是正辅助常数,
  • i = {0, 1, ….}

iii. 双重哈希

如果应用哈希函数h(k)后发生冲突,则会计算另一个哈希函数来查找下一个槽。

h(k, i) = (h1(k) + ih2(k)) mod m


好的哈希函数

好的哈希函数可能无法完全避免冲突,但可以减少冲突的数量。

在这里,我们将介绍查找好哈希函数的不同方法

1. 除法法

如果k是键,m是哈希表的大小,则哈希函数h()计算为

h(k) = k mod m

例如,如果哈希表的大小是10k = 112,则h(k) = 112 mod 10 = 2m的值不能是2的幂。这是因为2的幂的二进制格式是10, 100, 1000, …。当我们找到k mod m时,我们将始终得到低阶p位。

if m = 22, k = 17, then h(k) = 17 mod 22 = 10001 mod 100 = 01
if m = 23, k = 17, then h(k) = 17 mod 22 = 10001 mod 100 = 001
if m = 24, k = 17, then h(k) = 17 mod 22 = 10001 mod 100 = 0001
if m = 2p, then h(k) = p lower bits of m

2. 乘法法

h(k) = ⌊m(kA mod 1)⌋

其中,

  • kA mod 1给出小数部分kA
  • ⌊ ⌋给出下限值
  • A是任何常数。A的值介于0和1之间。但是,Knuth建议的最佳选择是≈ (√5-1)/2

3. 散列

在散列中,哈希函数是根据与键无关的方式随机选择的。


Python、Java 和 C/C++ 示例

# Python program to demonstrate working of HashTable 

# Initialize the hash table with 10 empty lists (each index is a list to handle collisions)
hashTable = [[] for _ in range(10)]

def checkPrime(n):
    if n == 1 or n == 0:
        return 0

    for i in range(2, n // 2):
        if n % i == 0:
            return 0

    return 1


def getPrime(n):
    if n % 2 == 0:
        n = n + 1

    while not checkPrime(n):
        n += 2

    return n


def hashFunction(key):
    capacity = getPrime(10)
    return key % capacity


def insertData(key, data):
    index = hashFunction(key)
    # Check if the key already exists in the list to update it, otherwise append
    found = False
    for i, kv in enumerate(hashTable[index]):
        if kv[0] == key:
            hashTable[index][i] = (key, data)  # Update existing key-value pair
            found = True
            break
    if not found:
        hashTable[index].append((key, data))  # Add new key-value pair if not found


def removeData(key):
    index = hashFunction(key)
    # Remove the key-value pair from the list if it exists
    for i, kv in enumerate(hashTable[index]):
        if kv[0] == key:
            del hashTable[index][i]
            break


# Test the hash table
insertData(123, "apple")
insertData(432, "mango")
insertData(213, "banana")
insertData(654, "guava")
insertData(213, "orange")  # This should update the value for key 213

print(hashTable)

removeData(123)

print(hashTable)
// Java program to demonstrate working of HashTable 

import java.util.*; 

class HashTable { 
  public static void main(String args[]) 
  {
  Hashtable<Integer, Integer> 
    ht = new Hashtable<Integer, Integer>(); 
  
  ht.put(123, 432); 
  ht.put(12, 2345);
  ht.put(15, 5643); 
  ht.put(3, 321);

  ht.remove(12);

  System.out.println(ht); 
  } 
} 
// Implementing hash table in C

#include <stdio.h>
#include <stdlib.h>

struct set
{
  int key;
  int data;
};
struct set *array;
int capacity = 10;
int size = 0;

int hashFunction(int key)
{
  return (key % capacity);
}
int checkPrime(int n)
{
  int i;
  if (n == 1 || n == 0)
  {
  return 0;
  }
  for (i = 2; i < n / 2; i++)
  {
  if (n % i == 0)
  {
    return 0;
  }
  }
  return 1;
}
int getPrime(int n)
{
  if (n % 2 == 0)
  {
  n++;
  }
  while (!checkPrime(n))
  {
  n += 2;
  }
  return n;
}
void init_array()
{
  capacity = getPrime(capacity);
  array = (struct set *)malloc(capacity * sizeof(struct set));
  for (int i = 0; i < capacity; i++)
  {
  array[i].key = 0;
  array[i].data = 0;
  }
}

void insert(int key, int data)
{
  int index = hashFunction(key);
  if (array[index].data == 0)
  {
  array[index].key = key;
  array[index].data = data;
  size++;
  printf("\n Key (%d) has been inserted \n", key);
  }
  else if (array[index].key == key)
  {
  array[index].data = data;
  }
  else
  {
  printf("\n Collision occured  \n");
  }
}

void remove_element(int key)
{
  int index = hashFunction(key);
  if (array[index].data == 0)
  {
  printf("\n This key does not exist \n");
  }
  else
  {
  array[index].key = 0;
  array[index].data = 0;
  size--;
  printf("\n Key (%d) has been removed \n", key);
  }
}
void display()
{
  int i;
  for (i = 0; i < capacity; i++)
  {
  if (array[i].data == 0)
  {
    printf("\n array[%d]: / ", i);
  }
  else
  {
    printf("\n key: %d array[%d]: %d \t", array[i].key, i, array[i].data);
  }
  }
}

int size_of_hashtable()
{
  return size;
}

int main()
{
  int choice, key, data, n;
  int c = 0;
  init_array();

  do
  {
  printf("1.Insert item in the Hash Table"
     "\n2.Remove item from the Hash Table"
     "\n3.Check the size of Hash Table"
     "\n4.Display a Hash Table"
     "\n\n Please enter your choice: ");

  scanf("%d", &choice);
  switch (choice)
  {
  case 1:

    printf("Enter key -:\t");
    scanf("%d", &key);
    printf("Enter data -:\t");
    scanf("%d", &data);
    insert(key, data);

    break;

  case 2:

    printf("Enter the key to delete-:");
    scanf("%d", &key);
    remove_element(key);

    break;

  case 3:

    n = size_of_hashtable();
    printf("Size of Hash Table is-:%d\n", n);

    break;

  case 4:

    display();

    break;

  default:

    printf("Invalid Input\n");
  }

  printf("\nDo you want to continue (press 1 for yes): ");
  scanf("%d", &c);

  } while (c == 1);
}
// Implementing hash table in C++

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

class HashTable
{
  int capacity;
  list<int> *table;

public:
  HashTable(int V);
  void insertItem(int key, int data);
  void deleteItem(int key);

  int checkPrime(int n)
  {
  int i;
  if (n == 1 || n == 0)
  {
    return 0;
  }
  for (i = 2; i < n / 2; i++)
  {
    if (n % i == 0)
    {
    return 0;
    }
  }
  return 1;
  }
  int getPrime(int n)
  {
  if (n % 2 == 0)
  {
    n++;
  }
  while (!checkPrime(n))
  {
    n += 2;
  }
  return n;
  }

  int hashFunction(int key)
  {
  return (key % capacity);
  }
  void displayHash();
};
HashTable::HashTable(int c)
{
  int size = getPrime(c);
  this->capacity = size;
  table = new list<int>[capacity];
}
void HashTable::insertItem(int key, int data)
{
  int index = hashFunction(key);
  table[index].push_back(data);
}

void HashTable::deleteItem(int key)
{
  int index = hashFunction(key);

  list<int>::iterator i;
  for (i = table[index].begin();
   i != table[index].end(); i++)
  {
  if (*i == key)
    break;
  }

  if (i != table[index].end())
  table[index].erase(i);
}

void HashTable::displayHash()
{
  for (int i = 0; i < capacity; i++)
  {
  cout << "table[" << i << "]";
  for (auto x : table[i])
    cout << " --> " << x;
  cout << endl;
  }
}

int main()
{
  int key[] = {231, 321, 212, 321, 433, 262};
  int data[] = {123, 432, 523, 43, 423, 111};
  int size = sizeof(key) / sizeof(key[0]);

  HashTable h(size);

  for (int i = 0; i < size; i++)
  h.insertItem(key[i], data[i]);

  h.deleteItem(12);
  h.displayHash();
}

哈希表应用

哈希表实现于

  • 需要常数时间查找和插入
  • 加密应用
  • 需要索引数据
你觉得这篇文章有帮助吗?

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战