散列表(三):冲突处理的方法之开地址法(线性探测再散列的实现)

二、开地址法

基本思想:当关键码key的哈希地址H0 = hash(key)出现冲突时,以H0为基础,产生另一个哈希地址H1 ,如果H1仍然冲突,再以H0

基础,产生另一个哈希地址H2 ,…,直到找出一个不冲突的哈希地址Hi ,将相应元素存入其中。这种方法有一个通用的再散列函

数形式:


其中H0 为hash(key) ,m为表长,di称为增量序列。增量序列的取值方式不同,相应的再散列方式也不同。主要有以下四种:

线性探测再散列

二次探测再散列

伪随机探测再散列

双散列法


(一)、线性探测再散列


假设给出一组表项,它们的关键码为 Burke, Ekers, Broad, Blum, Attlee, Alton, Hecht, Ederly。采用的散列函数是:取其第一个字母在

字母表中的位置。


hash (x) = ord (x) - ord (‘A’)

这样,可得

hash (Burke) = 1hash (Ekers) = 4

hash (Broad) = 1hash (Blum) = 1

hash (Attlee) = 0hash (Hecht) = 7

hash (Alton) = 0hash (Ederly) = 4

又设散列表为HT[26],m = 26。采用线性探查法处理溢出,则上述关键码在散列表中散列位置如图所示。红色括号内的数字表示找

到空桶时的探测次数。比如轮到放置Blum 的时候,探测位置1,被占据,接着向下探测位置2还是不行,最后放置在位置3,总的探

次数是3。



堆积现象

散列地址不同的结点争夺同一个后继散列地址的现象称为堆积(Clustering),比如ALton 本来位置是0,直到探测了6次才找到合适位

置5。这将造成不是同义词的结点也处在同一个探测序列中,从而增加了探测序列长度,即增加了查找时间。若散列函数不好、或装

填因子a 过大,都会使堆积现象加剧。


下面给出具体的实现代码,大体跟前面讲过的链地址法差异不大,只是利用的结构不同,如下:


status 保存状态,有EMPTY, DELETED, ACTIVE,删除的时候只是逻辑删除,即将状态置为DELETED,当插入新的key 时,只要不

是ACTIVE 的位置都是可以放入,如果是DELETED位置,需要将原来元素先释放free掉,再插入。


common.h:

C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef_COMMON_H_
#define_COMMON_H_

#include
#include
#include
#include
#include


#defineERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}\
while( 0)

#endif


hash.h:

C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef_HASH_H_
#define_HASH_H_

typedef structhashhash_t;
typedef unsigned int(*hashfunc_t)( unsigned int, void*);

hash_t*hash_alloc( unsigned intbuckets,hashfunc_thash_func);
voidhash_free(hash_t*hash);
void*hash_lookup_entry(hash_t*hash, void*key, unsigned intkey_size);
voidhash_add_entry(hash_t*hash, void*key, unsigned intkey_size,
void*value, unsigned intvalue_size);
voidhash_free_entry(hash_t*hash, void*key, unsigned intkey_size);


#endif /*_HASH_H_*/


hash.c:

C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include "hash.h"
#include "common.h"
#include


typedef enumentry_status
{
EMPTY,
ACTIVE,
DELETED
}entry_status_t;

typedef structhash_node
{
enumentry_statusstatus;
void*key;
void*value;
}hash_node_t;


structhash
{
unsigned intbuckets;
hashfunc_thash_func;
hash_node_t*nodes;
};

unsigned inthash_get_bucket(hash_t*hash, void*key);
hash_node_t*hash_get_node_by_key(hash_t*hash, void*key, unsigned intkey_size);


hash_t*hash_alloc( unsigned intbuckets,hashfunc_thash_func)
{
hash_t*hash=(hash_t*)malloc( sizeof(hash_t));
//assert(hash!=NULL);
hash->buckets=buckets;
hash->hash_func=hash_func;
intsize=buckets* sizeof(hash_node_t);
hash->nodes=(hash_node_t*)malloc(size);
memset(hash->nodes, 0,size);
printf( "Thehashtablehasallocate.\n");
returnhash;
}

voidhash_free(hash_t*hash)
{
unsigned intbuckets=hash->buckets;
inti;
for(i= 0;i {
if(hash->nodes[i].status!=EMPTY)
{
free(hash->nodes[i].key);
free(hash->nodes[i].value);
}
}

free(hash->nodes);

printf( "Thehashtablehasfree.\n");
}

void*hash_lookup_entry(hash_t*hash, void*key, unsigned intkey_size)
{
hash_node_t*node=hash_get_node_by_key(hash,key,key_size);
if(node== NULL)
{
return NULL;
}

returnnode->value;
}

voidhash_add_entry(hash_t*hash, void*key, unsigned intkey_size,
void*value, unsigned intvalue_size)
{
if(hash_lookup_entry(hash,key,key_size))
{
fprintf(stderr, "duplicatehashkey\n");
return;
}

unsigned intbucket=hash_get_bucket(hash,key);
unsigned inti=bucket;
//找到的位置已经有人存活,向下探测
while(hash->nodes[i].status==ACTIVE)
{
i=(i+ 1)%hash->buckets;
if(i==bucket)
{
//没找到,并且表满
return;
}
}

hash->nodes[i].status=ACTIVE;
if(hash->nodes[i].key) //释放原来被逻辑删除的项的内存
{
free(hash->nodes[i].key);
}
hash->nodes[i].key=malloc(key_size);
memcpy(hash->nodes[i].key,key,key_size);
if(hash->nodes[i].value) //释放原来被逻辑删除的项的内存
{
free(hash->nodes[i].value);
}
hash->nodes[i].value=malloc(value_size);
memcpy(hash->nodes[i].value,value,value_size);

}

voidhash_free_entry(hash_t*hash, void*key, unsigned intkey_size)
{
hash_node_t*node=hash_get_node_by_key(hash,key,key_size);
if(node== NULL)
return;

//逻辑删除,置标志位
node->status=DELETED;
}

unsigned inthash_get_bucket(hash_t*hash, void*key)
{
//返回哈希地址
unsigned intbucket=hash->hash_func(hash->buckets,key);
if(bucket>=hash->buckets)
{
fprintf(stderr, "badbucketlookup\n");
exit(EXIT_FAILURE);
}

returnbucket;
}

hash_node_t*hash_get_node_by_key(hash_t*hash, void*key, unsigned intkey_size)
{
unsigned intbucket=hash_get_bucket(hash,key);
unsigned inti=bucket;
while(hash->nodes[i].status!=EMPTY&&memcmp(key,hash->nodes[i].key,key_size)!= 0)
{
i=(i+ 1)%hash->buckets;
if(i==bucket) //探测了一圈
{
//没找到,并且表满
return NULL;
}
}
//比对正确,还得确认是否还存活
if(hash->nodes[i].status==ACTIVE)
{
return&(hash->nodes[i]);
}

//如果运行到这里,说明i为空位或已被删除

return NULL;
}

main.c:

C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include "hash.h"
#include "common.h"

typedef structstu
{
charsno[ 5];
charname[ 32];
intage;
}stu_t;

typedef structstu2
{
intsno;
charname[ 32];
intage;
}stu2_t;


unsigned inthash_str( unsigned intbuckets, void*key)
{
char*sno=( char*)key;
unsigned intindex= 0;

while(*sno)
{
index=*sno+ 4*index;
sno++;
}

returnindex%buckets;
}

unsigned inthash_int( unsigned intbuckets, void*key)
{
int*sno=( int*)key;
return(*sno)%buckets;
}

intmain( void)
{

stu2_tstu_arr[]=
{
{ 1234, "AAAA", 20},
{ 4568, "BBBB", 23},
{ 6729, "AAAA", 19}
};

hash_t*hash=hash_alloc( 256,hash_int);

intsize= sizeof(stu_arr)/ sizeof(stu_arr[ 0]);
inti;
for(i= 0;i {
hash_add_entry(hash,&(stu_arr[i].sno), sizeof(stu_arr[i].sno),
&stu_arr[i], sizeof(stu_arr[i]));
}

intsno= 4568;
stu2_t*s=(stu2_t*)hash_lookup_entry(hash,&sno, sizeof(sno));
if(s)
{
printf( "%d%s%d\n",s->sno,s->name,s->age);
}
else
{
printf( "notfound\n");
}

sno= 1234;
hash_free_entry(hash,&sno, sizeof(sno));
s=(stu2_t*)hash_lookup_entry(hash,&sno, sizeof(sno));
if(s)
{
printf( "%d%s%d\n",s->sno,s->name,s->age);
}
else
{
printf( "notfound\n");
}

hash_free(hash);

return 0;
}

simba@ubuntu:~/Documents/code/struct_algorithm/search/hash_table/linear_probing$ ./main
The hash table has allocate.
4568 BBBB 23
not found
The hash table has free.


与链地址法示例还有一点不同,就是key 使用的是int 类型,所以必须再实现一个hash_int 哈希函数,根据key 产生哈希地址。


你可能感兴趣的:(散列表(三):冲突处理的方法之开地址法(线性探测再散列的实现))