`
aspnetwinform
  • 浏览: 89805 次
  • 性别: Icon_minigender_2
  • 来自: 武汉
社区版块
存档分类
最新评论

散列表(四):冲突处理的方法之开地址法(二次探测再散列的实现)

 
阅读更多

前面的文章分析了开地址法的其中一种:线性探测再散列,这篇文章来讲开地址法的第二种:二次探测再散列


(二)、二次探测再散列

为改善“堆积”问题,减少为完成搜索所需的平均探查次数,可使用二次探测法。

通过某一个散列函数对表项的关键码 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:

C++ Code
<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:

C++ Code
<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_

typedefstructhashhash_t;
typedefunsignedint(*hashfunc_t)(unsignedint,void*);

hash_t*hash_alloc(unsignedintbuckets,hashfunc_thash_func);
voidhash_free(hash_t*hash);
void*hash_lookup_entry(hash_t*hash,void*key,unsignedintkey_size);
voidhash_add_entry(hash_t*hash,void*key,unsignedintkey_size,
void*value,unsignedintvalue_size);
voidhash_free_entry(hash_t*hash,void*key,unsignedintkey_size);


#endif/*_HASH_H_*/

hash.c:

C++ Code
<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>


typedefenumentry_status
{
EMPTY,
ACTIVE,
DELETED
}entry_status_t;

typedefstructhash_node
{
enumentry_statusstatus;
void*key;
unsignedintkey_size;//在拷贝进新的哈希表时有用
void*value;
unsignedintvalue_size;//在拷贝进新的哈希表时有用
}hash_node_t;


structhash
{
unsignedintbuckets;
unsignedintsize;//累加,如果size>buckets/2,则需要开裂建立新表
hashfunc_thash_func;
hash_node_t*nodes;
};

unsignedintnext_prime(unsignedintn);
intis_prime(unsignedintn);

unsignedinthash_get_bucket(hash_t*hash,void*key);
hash_node_t*hash_get_node_by_key(hash_t*hash,void*key,unsignedintkey_size);


hash_t*hash_alloc(unsignedintbuckets,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)
{
unsignedintbuckets=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,unsignedintkey_size)
{
hash_node_t*node=hash_get_node_by_key(hash,key,key_size);
if(node==NULL)
{
returnNULL;
}

returnnode->value;
}

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

unsignedintbucket=hash_get_bucket(hash,key);
unsignedinti=bucket;
unsignedintj=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。
//如果超出,必须将表长度扩充一倍,进行表的分裂。

unsignedintold_buckets=hash->buckets;

hash->buckets=next_prime(2*old_buckets);

hash_node_t*p=hash->nodes;
unsignedintsize;
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,unsignedintkey_size)
{
hash_node_t*node=hash_get_node_by_key(hash,key,key_size);
if(node==NULL)
return;

//逻辑删除
node->status=DELETED;
}

unsignedinthash_get_bucket(hash_t*hash,void*key)
{
unsignedintbucket=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,unsignedintkey_size)
{
unsignedintbucket=hash_get_bucket(hash,key);
unsignedinti=1;
unsignedintpos=bucket;
intodd=1;
unsignedinttmp=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一定能够插入,而且任何一个位置不会被探查两次。
//因此,只要表中至少有一半空的,就不会有表满问题。

returnNULL;
}

unsignedintnext_prime(unsignedintn)
{
//偶数不是质数
if(n%2==0)
{
n++;
}

for(;!is_prime(n);n+=2);//不是质数,继续求
returnn;
}

intis_prime(unsignedintn)
{
unsignedinti;
for(i=3;i*i<=n;i+=2)
{
if(n%i==0)
{
//不是,返回0
return0;
}
}

//是,返回1
return1;
}

main.c:

C++ Code
<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"

typedefstructstu
{
charsno[5];
charname[32];
intage;
}stu_t;

typedefstructstu2
{
intsno;
charname[32];
intage;
}stu2_t;


unsignedinthash_str(unsignedintbuckets,void*key)
{
char*sno=(char*)key;
unsignedintindex=0;

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

returnindex%buckets;
}

unsignedinthash_int(unsignedintbuckets,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);

return0;
}

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.



分享到:
评论

相关推荐

    散列表 (哈希表,线性探测再散列)

    - **二次探测再散列**:发生冲突时,按照一定的二次方公式来确定下一步的探测位置。 - **伪随机探测再散列**:发生冲突时,按照一种伪随机的方式寻找下一个空位。 - **再哈希法**:使用另一个哈希函数计算哈希地址...

    选取哈西函数h(k)=k%11,用线性探测在散列方法处理冲突。是

    选取哈西函数h(k)=k%11,用线性探测在散列方法处理冲突。是在0-10的散列地址中,对关键序列(22,41,53,46,30,01,67)构造哈希表并求等概率情 况下查找成功与不成功过的平均查找长度

    二次在探测散列表

    在本文中,我们将深入探讨二次探测散列表的概念、工作原理、实现方法以及其在C++中的应用。 首先,散列表是通过哈希函数将数据映射到一个固定大小的数组中,以实现快速的查找操作。然而,由于哈希函数的不完美性,...

    cpp代码-二次探测再散列哈希表

    二次探测再散列是一种解决哈希冲突的方法,当第一次哈希运算产生冲突时,不是立即采用链地址法或开放寻址法,而是进行第二次甚至多次探测,寻找下一个空的哈希位置。 在二次探测再散列中,如果哈希函数h(key)得到的...

    hash散列表的三种实现

    双重散列表(Double Hashing)是另一种解决哈希冲突的方法,它使用两个哈希函数,当第一次哈希计算后出现冲突,会使用第二个哈希函数计算偏移量,然后再去查找新的位置。这样可以避免线性探测法中的“聚集”问题,...

    数据结构散列表.ppt

    1. 开放地址法:当发生冲突时,寻找下一个空的散列地址,常见的有线性探测再散列、二次探测再散列和双哈希法等。 2. 链地址法:在每个散列地址上附加一个链表,所有散列到同一地址的关键码都挂在对应的链表上。 3. ...

    Hash表存储数据,用再散列来解决地址冲突问题,计算平均查找长度,还可以通过Key值查找对应的value

    1. 开放地址法:当发生冲突时,采用线性探测、二次探测或双哈希等方法寻找下一个空闲的位置。例如,线性探测就是顺序检查下一个位置,直到找到空位或者遍历完整个表。 2. 链地址法:在每个数组元素位置上附加一个...

    数据结构7.4散列查找技术

    1. 开放定址法:在散列表中寻找下一个空闲地址,可以是线性探测、二次探测或者双散列法。线性探测是向后寻找空闲地址,二次探测则是按照二次方的间隔寻找,双散列法使用另一个散列函数来计算探查序列。 2. 链接法:...

    线性开型寻址散列

    7. **优化**:为了提高性能,可能会使用开放寻址法的优化策略,比如二次探测(quadratic probing)或双哈希(double hashing)。这些方法在冲突发生时使用不同的步长,以期望更均匀地分散元素。 在源代码中,可能...

    哈希(散列)查找1

    3. 二次探测法:冲突时,按照二次项的位移量寻找下一个位置,如`h'(key) = (h(key) + di) mod m`,di是二次项的位移序列。这种方法试图通过二次步长避免聚集,但可能会循环回原位置。 4. 拉链法:将同义词(散列...

    散列表 课程设计

    - 开放地址法:当发生冲突时,通过线性探测、二次探测或双哈希等方法寻找下一个空位。 - 链地址法:每个数组元素是一个链表,冲突的键都存储在同一个链表中。 - 再哈希法:使用多个不同的哈希函数来解决冲突。 -...

    散列表之开放定址法

    2. 二次探测:线性探测的一种改进,冲突时按2的平方、3的平方(即+1, +4, +9...)的序列进行探测,这样可以更均匀地分布冲突元素,减少聚集。 3. 双哈希探测:利用第二个哈希函数来确定探测步长,使得探测序列更加...

    散列表相关学习代码示例

    2. **开放寻址法**:当发生冲突时,通过线性探测、二次探测或双哈希等方法寻找下一个空槽位,直到找到为止。这种方法要求数组必须预留一部分空间以应对冲突。 3. **链地址法**:在每个数组元素下挂接一个链表,所有...

    数据结构散列表电话号码查询系统课程设计

    2. **冲突处理**:使用二次探测再散列法处理冲突。 3. **输入输出函数**: - `getin()`:用于输入记录信息。 - `ShowInformation()`:显示用户的详细信息。 - `output()`:输出函数,用于展示查询结果。 4. **...

    数据结构课程设计-通讯录查询系统的设计与实现.docx

    在处理冲突时,采用二次探测再散列法。 d. 提供按电话号码查找并显示记录的功能。 e. 保存通讯录信息到文件,便于数据持久化。 **二、概要设计** 1. **数据结构**:系统使用两个结构体,`message` 用于存储单个...

    散列表(哈希表).

    - **开放寻址法**:通过一系列的探查策略(如线性探测、二次探测、伪随机探测)寻找空闲位置存放冲突的关键码。 - **再散列法**:使用多个散列函数,当发生冲突时,尝试使用其他函数直至找到可用位置。 - **链地址法...

    数据结构与算法课件:DS10_查找23.pptx

    闭散列表的算法实现则涉及到具体的冲突解决策略,比如线性探测再散列、二次探测再散列和双散列等。 负载因子α是衡量散列表性能的重要参数,它是填入表中的结点数n与散列表空间大小m的比值。负载因子过大,冲突概率...

Global site tag (gtag.js) - Google Analytics