一、简述哈希表
前言
数组的特点:访问地址、数据容易,但删除和插入困难;
而链表的特点:寻找地址困难,二插入和删除容易;
是否有可能取其精华,构造出一种二者优势兼具的数据结构呢?这便是哈希表
何为哈希表?
哈希表(Hash table)也叫做散列表,是根据关键码值(Key value)而直接进行访问的数据结构。即,它能够通过关键码值,依靠哈希函数处理记录,来映射到表中的一个位置地址来进行访问,以加快查找信息的速度。哈希函数也可以叫作散列函数,存放记录的数组也能叫作散列表。把key通过一个给定的算法将哈希函数转化为一个整形数据,根据数组的长度进行取余,然后把其作为一个数组的下标讲相应的value存储到数组空间里去,这种转化是一种压缩映射,即原来的空间经常远小于输入储存的空间,这就不可避免产生冲突,因此我们不仅要设计一种“好”的哈希表,也要设定一种解决冲突的方法。
哈希函数算法:
直接定址法、除留取余法、平方取中法、基数转化法、数字分析法、折叠法、随机数法
哈希冲突:
不同的关键字通过同一个哈希函数可能得到同一个哈希地址,即尽管key1 != key2,而Hash(key1)=Hash(key2)
其主要解决方法有:
……线性探测法
……二次探测法
……伪随机数序列
二、常用算法代码
链地址法:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define P 7 //宏定义
typedef int ElemType;//重命名
typedef struct HashNode//结点
{
ElemType data;
struct HashNode *link;//链地址法
}HashNode;
typedef HashNode* HashTalbe[P];//哈希表
void Init_HashTalbe(HashTalbe &ht);//初始化哈希表
int Hash(ElemType x); //哈希函数
void show_HashTalbe(HashTalbe &ht); //显示
void Insert_HashTable(HashTalbe &ht, ElemType x); //插入
void delete_HashTalbe(HashTalbe &ht, ElemType x); //删除
HashNode* Search_HashTalbe(HashTalbe &ht, ElemType x);//查找
void Init_HashTalbe(HashTalbe &ht)
{
for (int i = 0; i < P; i++)
{
ht[i] = NULL;
}
}
int Hash(ElemType x)
{
return x % P;
}
void Insert_HashTable(HashTalbe &ht, ElemType x)
{
int index = Hash(x);//获取对应地址索引
HashNode *p = (HashNode*)malloc(sizeof(HashNode));
assert(p != NULL);
p->data = x;
p->link = ht[index]; //新开辟结点指向该地址
ht[index] = p;
}
void delete_HashTalbe(HashTalbe &ht, ElemType x)
{
HashNode *p = Search_HashTalbe(ht, x);
if (p != NULL)
{
int index = Hash(x);
HashNode *q = ht[index];
if (p == q)
{
ht[index] = p->link;
free(p);
return;
}
while (q->link != p)
{
q = q->link;
}
q->link = p->link;
free(p);
}
}
void show_HashTalbe(HashTalbe &ht)
{
for (int i = 0; i < P; i++)
{
printf("%d:", i);
HashNode *p = ht[i];
while (p != NULL)
{
printf("%d->", p->data);
p = p->link;
}
printf("NUL.\n");
}
printf("\n");
}
HashNode* Search_HashTalbe(HashTalbe &ht, ElemType x)
{
int index = Hash(x);
HashNode *p = ht[index];
while (p != NULL&&p->data != x)
{
p = p->link;
}
return p;
}
int main()
{
HashTalbe ht;
Init_HashTalbe(ht);
ElemType array[] = { 19, 14, 23, 1, 68, 20, 84, 27, 55, 11, 10, 79 };
int n = sizeof(array) / sizeof(ElemType);
for (int i = 0; i < n; i++)
{
Insert_HashTable(ht, array[i]);
}
show_HashTalbe(ht);
ElemType key = 20;
HashNode *p = Search_HashTalbe(ht, key);
printf("p->data = %d\n\n", p->data);
delete_HashTalbe(ht, key);
show_HashTalbe(ht);
system("pause");
return 0;
}
#溢出桶
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define bucket_node_size 3
#define P 7
#define NULL_DATA -1
typedef struct bucket_node
{
int data[bucket_node_size];
struct bucket_node *next;
}bucket_node;
void init_bucket_node(bucket_node *hash_table)
{
for (int i = 0; i < P; i++)
{
for (int j = 0; j < bucket_node_size; j++)
{
hash_table[i].data[j] = NULL_DATA;
}
hash_table[i].next = NULL;
}
}
int hash(int x)
{
return x % P;
}
int insert(int x, bucket_node *hash_table)
{
int index = hash(x);
for (int i = 0; i < bucket_node_size; i++)
{
if (hash_table[index].data[i] == NULL_DATA)
{
hash_table[index].data[i] = x;
return 0;
};
}
//判断是否需要创建溢出桶
bucket_node *p = &hash_table[index];
while (p->next != NULL)
{
p = p->next;
for (int i = 0; i < bucket_node_size; ++i)
{
if (p->data[i] == NULL_DATA)
{
p->data[i] = x;
return 0;
}
}
}
bucket_node *s = (bucket_node*)malloc(sizeof(bucket_node));
assert(s != NULL);
for (int i = 0; i < bucket_node_size; i++)
{
s->data[i] = NULL_DATA;
}
s->next = NULL;
p->next = s;
s->data[0] = x;
return 0;
}
int main()
{
bucket_node hash_table[P];
init_bucket_node(hash_table);
int array[] = { 1, 8, 15, 22, 29, 36, 43 };
for (int i = 0; i < sizeof(array)/sizeof(int); i++)
{
//insertHashTable(ht, ar[i]);
insert(array[i], hash_table);
}
return 0;
}