hash冲突.docx
- 文档编号:6896454
- 上传时间:2023-01-12
- 格式:DOCX
- 页数:24
- 大小:25.80KB
hash冲突.docx
《hash冲突.docx》由会员分享,可在线阅读,更多相关《hash冲突.docx(24页珍藏版)》请在冰豆网上搜索。
hash冲突
/*
*hashquad.c开放定址法解决hash冲突问题
*/
#include
#include
#include"hashquad.h"
#include"fatal.h"
#defineMIN_TABLE_SIZE(10)
#defineREHASH_FACTOR (0.7)
enumkind_of_entry{legitimate,empty,deleted};
structhash_entry
{
element_type element;
enumkind_of_entry info;
};
typedefstructhash_entrycell;
structhash_tbl
{
inttable_size; /*表容量*/
intentry_cnt; /*已填入的单元数量*/
cell *cells;
};
staticint
need_rehash(hash_tableh)
{
return(h->entry_cnt>h->table_size*REHASH_FACTOR);
}
staticint
next_prime(intN)
{
inti;
if(N%2== 0)
N++;
for(;;N+=2){
for(i=3;i*i<=N;i+=2){
if(N%i==0)
gotocont_outer;
}
returnN;
cont_outer:
;
}
}
staticIndex
hash(element_typekey,inttable_size)
{
returnkey%table_size;
}
statichash_table
initialize_table(inttable_size)
{
hash_tableh;
inti;
if(table_size Error("Tablesizetoosmall! "); h=malloc(sizeof(structhash_tbl)); if(h==NULL) Error("Outofspace! "); h->table_size=next_prime(table_size); h->cells=malloc(sizeof(cell)*h->table_size); if(h->cells==NULL){ free(h); Error("Outofspace! "); } for(i=0;i h->cells[i].info=empty; } h->entry_cnt=0; returnh; } staticPos find(element_typekey,hash_tableh) { Pos cur_pos; int collision_num; collision_num=0; cur_pos=hash(key,h->table_size); /* 当hash表大部分被占满,这个过程可能很慢。 也有可能进入死循环! 需要在适当的时候进行rehash! */ while(h->cells[cur_pos].info! =empty && h->cells[cur_pos].element! =key){ cur_pos+=2*++collision_num-1; if(cur_pos>=h->table_size) cur_pos-=h->table_size; } returncur_pos; } staticvoid insert(element_typekey,hash_tableh) { Pos p; p=find(key,h); if(h->cells[p].info! =legitimate){ h->cells[p].info=legitimate; h->cells[p].element=key; h->entry_cnt++; } } statichash_table rehash(hash_tableh) { inti,old_size; cell*old_cells; old_cells=h->cells; old_size=h->table_size; /*创建一个新的doublesize的空表*/ h=initialize_table(2*old_size); /*拷贝旧表的数据到新表*/ for(i=0;i if(old_cells[i].info==legitimate) insert(old_cells[i].element,h); } free(old_cells); returnh; } /************ Thenextistestfunctions ***************/ #defineget_array_size(array) (sizeof(array)/sizeof(array[0])) void test_hashquad(void) { inti; Pos p; hash_tableh; element_typedatas[100]; for(i=0;i datas[i]=rand()%1000; } h=initialize_table(13); if(h==NULL) Error("Initializeerror! "); for(i=0;i insert(datas[i],h); /*在每次插入操作后检查是否需要rehash*/ if(need_rehash(h)) h=rehash(h); } p=find(67,h); if(h->cells[p].info! =legitimate){ printf("\n\tkeyvalue67notinhashtable! \n"); }else{ printf("\n\tkeyvalue67inhashtable! \n"); } p=find(datas[31],h); if(h->cells[p].info! =legitimate){ printf("\n\tkeyvalue%dnotinhashtable! \n",datas[31]); }else{ printf("\n\tkeyvalue%dinhashtable! \n",datas[31]); } i=i; return; } /*********************************************************************************************************/ /*hashquad.h*/ #ifndef_HASHQUAD_H_ #define_HASHQUAD_H_ typedefint element_type; typedefunsignedintIndex; typedefIndex Pos; structhash_tbl; typedefstructhash_tbl*hash_table; #endif 这是一个将整数的质因数分解算法: #include #include #include { boolflag; for(inti=int(sqrt((double)num));i>1;i--) { if((num%i)==0) flag=false; } if(! flag) { for(inti=int(sqrt(num));i>1;i--) { if((num%i)==0) { f(i); num=num/i; f(num); break; } } } else printf("%d\t",num); }intmain() { intnum; printf("pleaseinputanumber: "); scanf("%d",&num); f(num); return0; } 加颜色的部分为核心算法的C语言表述。 1、选择合适的算法和数据结构 应该熟悉算法语言,知道各种算法的优缺点,具体资料请参见相应的参考资料,有很多计算机书籍上都有介绍。 将比较慢的顺序查找法用较快的二分查找或乱序查找法代替,插入排序或冒泡排序法用快速排序、合并排序或根排序代替,都可以大大提高程序执行的效率。 .选择一种合适的数据结构也很重要,比如你在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。 数组与指针语句具有十分紧密的关系,一般来说,指针比较灵活简洁,而数组则比较直观,容易理解。 对于大部分的编译器,使用指针比使用数组生成的代码更短,执行效率更高。 但是在Keil中则相反,使用数组比使用的指针生成的代码更短。 。 2、使用尽量小的数据类型 能够使用字符型(char)定义的变量,就不要使用整型(int)变量来定义;能够使用整型变量定义的变量就不要用长整型(longint),能不使用浮点型(float)变量就不要使用浮点型变量。 当然,在定义变量后不要超过变量的作用范围,如果超过变量的范围赋值,C编译器并不报错,但程序运行结果却错了,而且这样的错误很难发现。 在ICCAVR中,可以在Options中设定使用printf参数,尽量使用基本型参数(%c、%d、%x、%X、%u和%s格式说明符),少用长整型参数(%ld、%lu、%lx和%lX格式说明符),至于浮点型的参数(%f)则尽量不要使用,其它C编译器也一样。 在其它条件不变的情况下,使用%f参数,会使生成的代码的数量增加很多,执行速度降低。 3、使用自加、自减指令 通常使用自加、自减指令和复合赋值表达式(如a-=1及a+=1等)都能够生成高质量的程序代码,编译器通常都能够生成inc和dec之类的指令,而使用a=a+1或a=a-1之类的指令,有很多C编译器都会生成二到三个字节的指令。 在AVR单片适用的ICCAVR、GCCAVR、IAR等C编译器以上几种书写方式生成的代码是一样的,也能够生成高质量的inc和dec之类的的代码。 4、减少运算的强度 可以使用运算量小但功能相同的表达式替换原来复杂的的表达式。 如下: (1)、求余运算。 a=a%8; 可以改为: a=a&7; 说明: 位操作只需一个指令周期即可完成,而大部分的C编译器的“%”运算均是调用子程序来完成,代码长、执行速度慢。 通常,只要求是求2n方的余数,均可使用位操作的方法来代替。 (2)、平方运算 a=pow(a,2.0); 可以改为: a=a*a; 说明: 在有内置硬件乘法器的单片机中(如51系列),乘法运算比求平方运算快得多,因为浮点数的求平方是通过调用子程序来实现的,在自带硬件乘法器的AVR单片机中,如ATMega163中,乘法运算只需2个时钟周期就可以完成。 既使是在没有内置硬件乘法器的AVR单片机中,乘法运算的子程序比平方运算的子程序代码短,执行速度快。 如果是求3次方,如: a=pow(a,3.0); 更改为: a=a*a*a; 则效率的改善更明显。 (3)、用移位实现乘除法运算 a=a*4; b=b/4; 可以改为: a=a<<2; b=b>>2; 说明: 通常如果需要乘以或除以2n,都可以用移位的方法代替。 在ICCAVR中,如果乘以2n,都可以生成左移的代码,而乘以其它的整数或除以任何数,均调用乘除法子程序。 用移位的方法得到代码比调用乘除法子程序生成的代码效率高。 实际上,只要是乘以或除以一个整数,均可以用移位的方法得到结果,如: a=a*9 可以改为: a=(a<<3)+a 5、循环 (1)、循环语 对于一些不需要循环变量参加运算的任务可以把它们放到循环外面,这里的任务包括表达式、函数的调用、指针运算、数组访问等,应该将没有必要执行多次的操作全部集合在一起,放到一个init的初始化程序中进行。 (2)、延时函数: 通常使用的延时函数均采用自加的形式: voiddelay(void) { unsignedinti; for(i=0;i<1000;i++) ; } 将其改为自减延时函数: voiddelay(void) { unsignedinti; for(i=1000;i>0;i--) ; } 两个函数的延时效果相似,但几乎所有的C编译对后一种函数生成的代码均比前一种代码少1~3个字节,因为几乎所有的MCU均有为0转移的指令,采用后一种方式能够生成这类指令。 在使用while循环时也一样,使用自减指令控制循环会比使用自加指令控制循环生成的代码更少1~3个字母。 但是在循环中有通过循环变量“i”读写数组的指令时,使用预减循环时有可能使数组超界,要引起注意。 (3)while循环和do…while循环 用while循环时有以下两种循环形式: unsignedinti; i=0; while(i<1000) { i++; //用户程序 } 或: unsignedinti; i=1000; do i--; //用户程序 while(i>0); 在这两种循环中,使用do…while循环编译后生成的代码的长度短于while循环。 6、查表 在程序中一般不进行非常复杂的运算,如浮点数的乘除及开方等,以及一些复杂的数学模型的插补运算,对这些即消耗时间又消费资源的运算,应尽量使用查表的方式,并且将数据表置于程序存储区。 如果直接生成所需的表比较困难,也尽量在启了,减少了程序执行过程中重复计算的工作量。 7、其它 比如使用在线汇编及将字符串和一些常量保存在程序存储器中,均有利于优化。 enum类型的本质 至从C语言开始enum类型就被作为用户自定义分类有限集合常量的方法被引入到了语言 当中,而且一度成为C++中定义编译期常量的唯一方法(后来在类中引入了静态整型常量)。 根据上面对enum类型的描述,到底enum所定义出来的类型是一个什么样的类型呢? 作为 一个用户自定义的类型其所占用的内存空间是多少呢? 使用enum类型是否真的能够起到有限 集合常量的边界约束呢? 大家可能都知道enum类型和int类型具有隐示(自动)转换的规则, 那么是否真的在任何地方都可以使用enum类型的变量来代替int类型的变量呢? 下面会逐一 回答这些问题。 1.到底enum所定义出来的类型是一个什么样的类型呢? 在C++中大家都知道仅仅有两种大的类型分类: POD类型和类类型(不清楚的可以参 见我的其他文章)。 enum所定义的类型其实属于POD类型,也就是说它会参与到POD 类型的隐示转换规则当中去,所以才会出现enum类型与int类型之间的隐示转换现象。 那么也就是说enum所定义的类型不具备名字空间限定能力(因为不属于类类型), 其所定义的常量子具备和enum类型所在名字空间相同的可见性,由于自身没有名字 限定能力,所以会出现名字冲突现象。 如: structCEType { enumEType1{e1,e2}; enumEType2{e1,e2}; }; 上面的例子会出现e1、e2名字冲突编译时错误,原因就在于枚举子(e1、e2)是 CEType名字空间中的名字,同样在引用该CEType中的枚举子时必须采用CEType: : e1 这样的方式进行,而不是CEType: : EType1: : e1来进行引用。 2.作为一个用户自定义的类型其所占用的内存空间是多少呢? 该问题就是sizeof(EType1)等于多少的问题,是不是每一个用户自定义的枚举类 型都具有相同的尺寸呢? 在大多数的32位编译器下(如: VC++、gcc等)一个枚举类 型的尺寸其实就是一个sizeof(int)的大小,难道枚举类型的尺寸真的就应该是int 类型的尺寸吗? 其实不是这样的,在C++标准文档(ISO14882)中并没有这样来定义, 标准中是这样说明的: “枚举类型的尺寸是以能够容纳最大枚举子的值的整数的尺寸”, 同时标准中也说名了: “枚举类型中的枚举子的值必须要能够用一个int类型表述”, 也就是说,枚举类型的尺寸不能够超过int类型的尺寸,但是是不是必须和int类型 具有相同的尺寸呢? 上面的标准已经说得很清楚了,只要能够容纳最大的枚举子的 值的整数就可以了,那么就是说可以是char、short和int。 例如: enumEType1{e1=CHAR_MAX}; enumEType2{e2=SHRT_MAX}; enumEType3{e3=INT_MAX }; 上面的三个枚举类型分别可以用char、short、int的内存空间进行表示,也就是: sizeof(EType1)==sizeof(char ); sizeof(EType2)==sizeof(short); sizeof(EType3)==sizeof(int ); 那为什么在32位的编译器下都会将上面三个枚举类型的尺寸编译成int类型的尺寸呢? 主要是从32位数据内存对其方面的要求进行考虑的,在某些计算机硬件环境下具有对 齐的强制性要求(如: sunSPARC),有些则是因为采用一个完整的32位字长CPU处理 效率非常高的原因(如: IA32)。 所以不可以简单的假设枚举类型的尺寸就是int类 型的尺寸,说不定会遇到一个编译器为了节约内存而采用上面的处理策略。 3.使用enum类型是否真的能够起到有限集合常量的边界约束呢? 首先看一下下面这个例子: enumEType{e1=0,e2}; voidfunc1(ETypee) { if(e==e1) { //dosomething } //dosomethingbecausee! =e1muste==e2 } voidfunc2(ETypee) { if(e==e1) { //dosomething } elseif(e==e2)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- hash 冲突