数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表,哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法(链式哈希表)
[1]hash函数定义
根据key,计算出key对应记录的储存位置
position = f(key)
函数f就是hash函数
[2]hash冲突定义
不同的key可能得到相同的position。原因为key值的范围过大。如int.
如果构造一个完全没有冲突的表。那么对应的地址空间就是4G,这是根本不
实现的。为了处理这个问题。就允许不同的key可以有相同的position。
[3]hash表定义
映射的集合
[4]设计一个好的hash函数非常重要
由[2]可知,一个好的hash函数可以使positions集中分布在某一段内存地址范围内。
并且position在这段地址范围内存尽量均匀。这样每个key对应的position就尽可能
少了,即hash冲突少。查找就会快一点。
[5]所谓的hash桶定义
就是预分配的一段连续的储存空间
[6]处理冲突的方法
(1)开放定址法:就是先指定一个桶,然后position = f(key,d),如果postion已经存在了,改变参数d,
继续计算position = f(key,d),直到postion不存在
d的不同取值的算法有:线性探测再散列,二次探测再散列,伪随机探测再散列。
听这名字觉得好高深哦。实际上, 就是为了更高效地计算出没有使用的position
(2)链地址法
就是把position相同的item按插入的先后顺序组成一个链表
根据key,计算出key对应记录的储存位置
position = f(key)
函数f就是hash函数
[2]hash冲突定义
不同的key可能得到相同的position。原因为key值的范围过大。如int.
如果构造一个完全没有冲突的表。那么对应的地址空间就是4G,这是根本不
实现的。为了处理这个问题。就允许不同的key可以有相同的position。
[3]hash表定义
映射的集合
[4]设计一个好的hash函数非常重要
由[2]可知,一个好的hash函数可以使positions集中分布在某一段内存地址范围内。
并且position在这段地址范围内存尽量均匀。这样每个key对应的position就尽可能
少了,即hash冲突少。查找就会快一点。
[5]所谓的hash桶定义
就是预分配的一段连续的储存空间
[6]处理冲突的方法
(1)开放定址法:就是先指定一个桶,然后position = f(key,d),如果postion已经存在了,改变参数d,
继续计算position = f(key,d),直到postion不存在
d的不同取值的算法有:线性探测再散列,二次探测再散列,伪随机探测再散列。
听这名字觉得好高深哦。实际上, 就是为了更高效地计算出没有使用的position
(2)链地址法
就是把position相同的item按插入的先后顺序组成一个链表
经典的字符串哈希函数
/*hashpjw.c*/
unsigned int hashpjw(const void *key)
{
const char *ptr;
unsigned int val;
val = 0;
ptr = key;
while (*ptr != '\0'){
unsigned int tmp;
val = (val << 4) +(*ptr);
if (tmp = (val & 0xf0000000)){
val = val ^ (tmp >> 24);
val = val ^ tmp;
}
ptr++;
}
return val%PRIME_TBLSIZ //桶的个数
}
由上图可以看到 :由数组+链表组成的
链式哈希表定于与实现
//
// Chtbl.h
// Algorithms&Data_structures
//
// Created by TTc on 15-2-3.
// Copyright (c) 2015年 TTc. All rights reserved.
//
#ifndef __Algorithms_Data_structures__Chtbl__
#define __Algorithms_Data_structures__Chtbl__
#include <stdlib.h>
/*单链表的头文件*/
#include "List.h"
/* 定义一个链式哈希表的结构体*/
typedef struct Chtbl_ {
int buckets; /*哈希表分配桶的个数*/
int (*h)(const void *key);/*哈希函数指针h */
int (*match)(const void *key1,const void *key2);/*判断2个键是否匹配*/
void (*destroy)(void *data);/*释放内存空间*/
int size;/*成员个数*/
List *table;/*哈希表中链表桶的头指针*/
}Chtbl;
/* 函数接口*/
int chtbl_init (Chtbl *htbl,int buckets,int (*h)(const void *key),int
(*match)(const void *key1,const void *key2),void (*destroy)(void *data));
int chtbl_destroy (Chtbl *htbl);
int chtbl_insert(Chtbl *htbl,const void *data);
int chtbl_remove (Chtbl *htbl,void **data);
int chtbl_lookup(const Chtbl *htbl,void **data);
#define chtbl_size (Chtbl *htbl) ((htbl)->size)
#endif /* defined(__Algorithms_Data_structures__Chtbl__) */
//
// Chtbl.c
// Algorithms&Data_structures
//
// Created by TTc on 15-2-3.
// Copyright (c) 2015年 TTc. All rights reserved.
//
#include "Chtbl.h"
#include <stdio.h>
#include <string.h>
/*初始化 哈希表结构体*/
int
chtbl_init(Chtbl *htbl,int buckets,int (*h)(const void *key),
int (*match)(const void *key1,const void *key2),void (*destroy)(void *data)){
int i ;
/* 申请空间为桶(实质是一个单链表)*/
if((htbl->table = (List*)malloc(buckets * sizeof(List))) == NULL ){
return -1;
}
/*初始化 哈希表*/
htbl->buckets = buckets;
for (i=0; i<buckets; i++) {
list_init(&htbl->table[i], destroy);
}
htbl->h = h;
htbl->match = match;
htbl->destroy = destroy;
htbl->size = 0;
return 0;
}
int
chtbl_destroy (Chtbl *htbl){
int i ;
for (i=0; i < htbl->buckets; i++) {
list_destroy(&htbl->table[i]);
}
free(htbl->table);
memset(htbl, 0, sizeof(Chtbl));
return 0;
}
/* 插入数据
* return 成功返回0;失败返回-1;存在返回1;
*
*/
int
chtbl_insert(Chtbl *htbl,const void *data){
void *temp;
int bucket;
int retval;
//Do nothing if the data is already in the table
temp = (void*)data;
if(chtbl_lookup(htbl, &temp) == 0)
return 1;
//Hash the key
bucket = htbl->h(data) % htbl->buckets;
//Insery the data into the buckets
/*插入哈希表*/
if ((retval = list_ins_next(&htbl->table[bucket], NULL, data)) == 0)
htbl ->size ++;
return retval;
}
/*删除数据
*return 成功返回0;失败返回-1;
*/
int
chtbl_remove (Chtbl *htbl,void **data){
ListElmt *element, *prev;
int bucket;
//Hash the key
/*获得哈希键*/
bucket = htbl->h(*data) % htbl->buckets;
//search for the data in the bucket .
prev = NULL;
/*查找在哈希表中的data*/
for (element = list_head(&htbl->table[bucket]); element != NULL; element = list_next(element)) {
if(htbl->match(*data,list_data(element))){
//Remove the data from the bucket
if(list_rem_next(&htbl->table[bucket], prev, list_data(element)) == 0){
htbl->size --;
return 0;
} else {
return -1;
}
}
prev = element;
}
//returen that the data was not found.
return -1;
}
/* 在哈希表中查找元素
*return :如果找到元素返回0;否则返回-1.
*/
int
chtbl_lookup(const Chtbl *htbl,void **data){
ListElmt *element;
int bucket;
//Hash the key
bucket = htbl->h(*data) % htbl->buckets;
//search for the data in the bucket .
for (element = list_head(&htbl->table[bucket]); element != NULL; element = list_next(element)) {
if(htbl->match(*data,list_data(element))){
//Pass back the data from the table
*data = list_data(element);
return 0;
}
}
//returen that the data was not found.
return -1;
}