前面的文章分析了开地址法的其中一种:线性探测再散列,这篇文章来讲开地址法的第二种:二次探测再散列
(二)、二次探测再散列
为改善“堆积”问题,减少为完成搜索所需的平均探查次数,可使用二次探测法。
通过某一个散列函数对表项的关键码 x 进行计算,得到桶号,它是一个非负整数。
若设表的长度为TableSize = 23,则在线性探测再散列举的例子中利用二次探查法所得到的散列结果如图所示。
比如轮到放置Blum 的时候,本来应该是位置1,已经被Burke 占据,接着探测 H0 + 1 = 2,,发现被Broad 占据,接着探测 H0 - 1 =
0,发现空位于是放进去,探测次数为3。
下面来看具体代码实现,跟前面讲过的线性探测再散列差不多,只是探测的方法不同,但使用的数据结构也有点不一样,此外还实
现了开裂,如果装载因子 a > 1/2; 则建立新表,将旧表内容拷贝过去,所以hash_t 结构体需要再保存一个size 成员,同样的原因,
为了将旧表内容拷贝过去,hash_node_t 结构体需要再保存 *key 和 *value 的size。
common.h:
<nobr>1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br> 17<br> 18<br> 19<br></nobr>
|
#ifndef_COMMON_H_
#define_COMMON_H_ #include<unistd.h> #include<sys/types.h> #include<stdlib.h> #include<stdio.h> #include<string.h> #defineERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }\ while( 0) #endif |
hash.h:
<nobr>1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br></nobr>
|
#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:
<nobr>1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br> 17<br> 18<br> 19<br> 20<br> 21<br> 22<br> 23<br> 24<br> 25<br> 26<br> 27<br> 28<br> 29<br> 30<br> 31<br> 32<br> 33<br> 34<br> 35<br> 36<br> 37<br> 38<br> 39<br> 40<br> 41<br> 42<br> 43<br> 44<br> 45<br> 46<br> 47<br> 48<br> 49<br> 50<br> 51<br> 52<br> 53<br> 54<br> 55<br> 56<br> 57<br> 58<br> 59<br> 60<br> 61<br> 62<br> 63<br> 64<br> 65<br> 66<br> 67<br> 68<br> 69<br> 70<br> 71<br> 72<br> 73<br> 74<br> 75<br> 76<br> 77<br> 78<br> 79<br> 80<br> 81<br> 82<br> 83<br> 84<br> 85<br> 86<br> 87<br> 88<br> 89<br> 90<br> 91<br> 92<br> 93<br> 94<br> 95<br> 96<br> 97<br> 98<br> 99<br> 100<br> 101<br> 102<br> 103<br> 104<br> 105<br> 106<br> 107<br> 108<br> 109<br> 110<br> 111<br> 112<br> 113<br> 114<br> 115<br> 116<br> 117<br> 118<br> 119<br> 120<br> 121<br> 122<br> 123<br> 124<br> 125<br> 126<br> 127<br> 128<br> 129<br> 130<br> 131<br> 132<br> 133<br> 134<br> 135<br> 136<br> 137<br> 138<br> 139<br> 140<br> 141<br> 142<br> 143<br> 144<br> 145<br> 146<br> 147<br> 148<br> 149<br> 150<br> 151<br> 152<br> 153<br> 154<br> 155<br> 156<br> 157<br> 158<br> 159<br> 160<br> 161<br> 162<br> 163<br> 164<br> 165<br> 166<br> 167<br> 168<br> 169<br> 170<br> 171<br> 172<br> 173<br> 174<br> 175<br> 176<br> 177<br> 178<br> 179<br> 180<br> 181<br> 182<br> 183<br> 184<br> 185<br> 186<br> 187<br> 188<br> 189<br> 190<br> 191<br> 192<br> 193<br> 194<br> 195<br> 196<br> 197<br> 198<br> 199<br> 200<br> 201<br> 202<br> 203<br> 204<br> 205<br> 206<br> 207<br> 208<br> 209<br> 210<br> 211<br> 212<br> 213<br> 214<br> 215<br> 216<br> 217<br> 218<br> 219<br> 220<br> 221<br> 222<br> 223<br> 224<br> 225<br> 226<br> 227<br> 228<br> 229<br> 230<br> 231<br> 232<br> 233<br> 234<br> 235<br> 236<br> 237<br> 238<br> 239<br> 240<br> 241<br> 242<br> 243<br> 244<br> 245<br> 246<br> 247<br> 248<br> 249<br> 250<br> 251<br> 252<br> 253<br> 254<br> 255<br> 256<br> 257<br> 258<br> 259<br> 260<br> 261<br> 262<br> 263<br> 264<br> 265<br> 266<br> 267<br> 268<br> 269<br> 270<br> 271<br> 272<br> 273<br> 274<br> 275<br> 276<br> 277<br> 278<br> 279<br> 280<br></nobr>
|
#include
"hash.h"
#include "common.h" #include<assert.h> typedef enumentry_status { EMPTY, ACTIVE, DELETED }entry_status_t; typedef structhash_node { enumentry_statusstatus; void*key; unsigned intkey_size; //在拷贝进新的哈希表时有用 void*value; unsigned intvalue_size; //在拷贝进新的哈希表时有用 }hash_node_t; structhash { unsigned intbuckets; unsigned intsize; //累加,如果size>buckets/2,则需要开裂建立新表 hashfunc_thash_func; hash_node_t*nodes; }; unsigned intnext_prime( unsigned intn); intis_prime( unsigned intn); 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<buckets;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; unsigned intj=i; intk= 1; intodd= 1; while(hash->nodes[i].status==ACTIVE) { if(odd) { i=j+k*k; odd= 0; //i%hash->buckets; while(i>=hash->buckets) { i-=hash->buckets; } } else { i=j-k*k; odd= 1; while(i< 0) { i+=hash->buckets; } ++k; } } hash->nodes[i].status=ACTIVE; if(hash->nodes[i].key) 释放原来被逻辑删除的项的内存 { free(hash->nodes[i].key); } hash->nodes[i].key=malloc(key_size); hash->nodes[i].key_size=key_size; //保存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); hash->nodes[i].value_size=value_size; //保存value_size; memcpy(hash->nodes[i].value,value,value_size); if(++(hash->size)<hash->buckets/ 2) return; //在搜索时可以不考虑表装满的情况; //但在插入时必须确保表的装填因子不超过0.5。 //如果超出,必须将表长度扩充一倍,进行表的分裂。 unsigned intold_buckets=hash->buckets; hash->buckets=next_prime( 2*old_buckets); hash_node_t*p=hash->nodes; unsigned intsize; hash->size= 0; //从0 开始计算 size= sizeof(hash_node_t)*hash->buckets; hash->nodes=(hash_node_t*)malloc(size); memset(hash->nodes, 0,size); for(i= 0;i<old_buckets;i++) { if(p[i].status==ACTIVE) { hash_add_entry(hash,p[i].key,p[i].key_size,p[i].value,p[i].value_size); } } for(i= 0;i<old_buckets;i++) {
// active or deleted
if(p[i].key) { free(p[i].key); } if(p[i].value) { free(p[i].value); } } free(p); //释放旧表 } 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= 1; unsigned intpos=bucket; intodd= 1; unsigned inttmp=pos; while(hash->nodes[pos].status!=EMPTY&&memcmp(key,hash->nodes[pos].key,key_size)!= 0) { if(odd) { pos=tmp+i*i; odd= 0; //pos%hash->buckets; while(pos>=hash->buckets) { pos-=hash->buckets; } } else { pos=tmp-i*i; odd= 1; while(pos< 0) { pos+=hash->buckets; } i++; } } if(hash->nodes[pos].status==ACTIVE) { return&(hash->nodes[pos]); } //如果运行到这里,说明pos为空位或者被逻辑删除 //可以证明,当表的长度hash->buckets为质数且表的装填因子不超过0.5时, //新的表项x一定能够插入,而且任何一个位置不会被探查两次。 //因此,只要表中至少有一半空的,就不会有表满问题。 return NULL; } unsigned intnext_prime( unsigned intn) { //偶数不是质数 if(n% 2== 0) { n++; } for(;!is_prime(n);n+= 2); //不是质数,继续求 returnn; } intis_prime( unsigned intn) { unsigned inti; for(i= 3;i*i<=n;i+= 2) { if(n%i== 0) { //不是,返回0 return 0; } } //是,返回1 return 1; } |
main.c:
<nobr>1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br> 17<br> 18<br> 19<br> 20<br> 21<br> 22<br> 23<br> 24<br> 25<br> 26<br> 27<br> 28<br> 29<br> 30<br> 31<br> 32<br> 33<br> 34<br> 35<br> 36<br> 37<br> 38<br> 39<br> 40<br> 41<br> 42<br> 43<br> 44<br> 45<br> 46<br> 47<br> 48<br> 49<br> 50<br> 51<br> 52<br> 53<br> 54<br> 55<br> 56<br> 57<br> 58<br> 59<br> 60<br> 61<br> 62<br> 63<br> 64<br> 65<br> 66<br> 67<br> 68<br> 69<br> 70<br> 71<br> 72<br> 73<br> 74<br> 75<br> 76<br> 77<br> 78<br> 79<br> 80<br> 81<br> 82<br> 83<br> 84<br> 85<br></nobr>
|
#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<size;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/quardratic_probing$ ./main
The hash table has allocate.
4568 BBBB 23
not found
The hash table has free.