哈希表:
顺序搜索以及二叉树搜索树中,元素存储位置和元素各关键码之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。搜索的效率取决于搜索过程中元素的比较次数。 理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。 当向该结构中: 插入元素时:根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放搜索元素时:对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键 码相等,则搜索成功 该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(Hash Table)(或者 称散列表)
哈希碰撞
HashFun(Ki) == HashFun(Kj)即不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。
解决哈希冲突两种常见的方法是:闭散列和开散列
闭散列:也叫开放地址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到表中 “下一个”空位中去 那如何寻找下一个空余位置? 线性探测 设关键码集合为{37, 25, 14, 36, 49, 68, 57,11},散列表为HT[12],表的大小m = 12,假设哈希函数为:Hash(x) = x % p(p = 11,是接近m的质数),就有: Hash(37) = 4 Hash(25) = 3 Hash(14) = 3 Hash(36) = 3 Hash(49) =5 Hash(68) = 2 Hash(57) = 2 Hash(11) = 0 其中25,14,36以及68,57发生哈希冲突,一旦冲突必须要找出下一个空余位置 线性探测找的处理为:从发生冲突的位置开始,依次继续向后探测,直到找到空位置为止 【插入】 1.使用哈希函数找到待插入元素在哈希表中的位置 2.如果该位置中没有元素则直接插入新元素;如果该位置中 有元素且和待插入元素相同,则不用插入;如果该位置中有元素但不是待插入元素则发生哈希冲突,使用线性探测找到下一个空位置,插入新元素; 采用线性探测处理哈希冲突: 采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索。为什么?那该 怎么来删除? 采用线性探测,实现起来非常简单,缺陷是: 一旦发生哈希冲突,所有的冲突连在一起,容易产生数据“堆积”,即:不同关键码占据了可利用的空位置,使得寻找某关键码的位置需要许多次比较,导致搜索效率降低。
闭散列(开放地址法)的实现:
Hash.h
#pragma once
typedef int KeyType;
typedef int ValueType;
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<malloc.h>
typedef enum Status
{
EMPTY,
EXITS,
DELET,
}Status;
typedef struct HashNode
{
KeyType _key;
ValueType _value;
Status _status;
}HashNode;
typedef struct HashTable
{
HashNode* _tables;
size_t _size;
size_t _N;
}HashTable;
size_t GetprimerNext(size_t cur);
void HashTablePrint(HashTable*ht);
void HashTableInit(HashTable* ht,size_t size);//初始化
int HashTableInsert(HashTable* ht, KeyType key, ValueType value);//插入
HashNode* HashTableFind(HashTable*ht, KeyType key);//查找
int HashTableRemove(HashTable*ht, KeyType key);//删除
void HashTableDestroy(HashTable*ht);//摧毁
hash.c
#include"Hash.h"
void HashTableInit(HashTable* ht,size_t size)//初始化
{
assert(ht);
ht->_size =0;
ht->_N = size;
ht->_tables=(HashNode*)malloc(sizeof(HashNode)*(ht->_N));
assert(ht->_tables);
size_t i = 0;
for (i=0; i < ht->_N; i++)
{
ht->_tables[i]._status=EMPTY;
}
}
size_t GetprimerNext(size_t cur)
{
static const unsigned long _PrimeList[28] =
{
53ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
};
for (int i = 0; i < 28; i++)
{
if (cur < _PrimeList[i])
{
cur = _PrimeList[i];
return cur;
}
}
return _PrimeList[27];
}
size_t FunC(KeyType key, size_t N)
{
return key%N;//计算元素所在的下标
}
int HashTableInsert(HashTable* ht, KeyType key, ValueType value)//插入
{
assert(ht);
if (ht->_size*10 / ht->_N>7)
{
//增容
size_t newN = GetprimerNext(ht->_N);
HashTable newht;
HashTableInit(&newht,newN);
for (size_t i = 0; i <ht->_size; i++)
{
HashTableInsert(&newht, ht->_tables[i]._key, ht->_tables[i]._value);
}
free(ht->_tables);
ht->_N = newN;
ht->_tables = newht._tables;
}
size_t index = FunC(key, ht->_N);
while (ht->_tables[index]._status==EXITS)
{
if (key == ht->_tables[index]._key)
{
return -1;
}
index++;
if (ht->_size > ht->_N)
{
index = 0;
}
}
ht->_tables[index]._key = key;
ht->_tables[index]._value = value;
ht->_tables[index]._status = EXITS;
ht->_size++;
return 0;
}
void HashTablePrint(HashTable *ht)
{
assert(ht);
for (size_t i = 0; i < ht->_N; i++)
{
if (ht->_tables[i]._status==EXITS)
printf("[%d]->%d\n ",i, ht->_tables[i]._key);
if (ht->_tables[i]._status != EXITS)
printf("[%d]->%s\n", i," NULL");
}
printf("\n");
}
HashNode* HashTableFind(HashTable*ht, KeyType key)//查找
{
assert(ht);
size_t index = FunC(key, ht->_N);
if (key == ht->_tables[index]._key)
{
return &(ht->_tables[index]);
}
else
{
size_t i = 0;
while (i < ht->_N)
{
if (ht->_size>ht->_N)
{
index = 0;
}
index += i;
if (key == ht->_tables[index]._key)
return &(ht->_tables[index]);
if (ht->_tables[index]._status == EMPTY)
return NULL;
i++;
}
}
return NULL;
}
int HashTableRemove(HashTable*ht, KeyType key)//删除
{
/*if (HashTableFind(&ht, key) != NULL)
{
HashTableFind(&ht, key)->_status = DELET;
return 0;
}
else
return -1;*/
assert(ht);
size_t index = FunC(key, ht->_N);
if (key == ht->_tables[index]._key)
{
ht->_tables[index]._status =DELET;
}
else
{
size_t i = 0;
for (i = 0; i < ht->_N; i++)
{
if (ht->_size>ht->_N)
index = 0;
index += i;
if (key == ht->_tables[index]._key)
{
ht->_tables[index]._status =DELET;
return 0;
}
}
return -1;
}
return -1;
}
void HashTableDestroy(HashTable*ht)
{
assert(ht);
free(ht->_tables);
ht->_tables = NULL;
ht->_size = 0;
ht->_N = 0;
}
test.c
#include"Hash.h"
void Hashtest()
{
HashTable ht;
HashTableInit(&ht,11);
HashTableInsert(&ht, 37, 0);
HashTableInsert(&ht, 25, 0);
HashTableInsert(&ht, 14, 0);
HashTableInsert(&ht, 11, 0);
HashTableInsert(&ht, 49, 0);
HashTableInsert(&ht, 68, 0);
HashTableInsert(&ht, 57, 0);
HashTableInsert(&ht, 36, 0);
//HashTablePrint(&ht);
printf("\n");
HashTableInsert(&ht, 1, 0);
HashTableInsert(&ht, 2, 0);
HashTableInsert(&ht, 3, 0);
HashTableInsert(&ht, 34, 0);
HashTableInsert(&ht, 33, 0);
HashTablePrint(&ht);
printf("%d\n ",HashTableFind(&ht, 36)->_key);
printf("%d\n ", HashTableFind(&ht, 3)->_key);
printf("%d\n ", HashTableFind(&ht,49 )->_key);
printf("%d\n ", HashTableFind(&ht,33 )->_key);
printf("%d\n ", HashTableFind(&ht, 11)->_key);
printf("%d\n ", HashTableFind(&ht, 68)->_key);
printf("%d\n ", HashTableFind(&ht, 57)->_key);
printf("%p\n ", HashTableFind(&ht, 44));
HashTableRemove(&ht, 68);
HashTableRemove(&ht, 57);
HashTableRemove(&ht, 11);
HashTableRemove(&ht, 33);
//HashTableRemove(&ht, 25);
//HashTableRemove(&ht, 14);
//HashTableRemove(&ht, 49);
//HashTableRemove(&ht, 33);
HashTablePrint(&ht);
//HashTableDestroy(&ht);
//HashTablePrint(&ht);
}
int main()
{
Hashtest();
system("pause");
return 0;
}