哈希表
c 语言 哈希表
C语言中的哈希表是一种数据结构,用于实现键-值对的存储和检索。
它通过将键映射到数组中的索引来实现高效的查找操作。
在C语言中,哈希表通常使用数组和链表的组合来实现。
一般情况下,哈希表包含一个固定大小的数组,每个数组元素称为一个桶(bucket)。
哈希函数将键映射到数组索引,每个键的哈希函数计算结果都应该是唯一的。
当需要插入一个键值对时,首先通过哈希函数计算键的哈希值,然后将键值对插入到对应索引的桶中。
如果多个键映射到相同的索引,这种情况称为哈希冲突。
为了解决冲突,每个桶通常是一个链表,新的键值对会被插入到链表的头部。
当需要查找一个键时,通过哈希函数计算键的哈希值,然后查找对应索引的桶。
在桶的链表中逐个比较键,直到找到对应的值或者链表结束。
需要注意的是,在哈希表中,哈希函数的设计非常重要,它应该尽可能均匀地将键分布到数组索引中,以减少哈希冲突的概率。
此外,当哈希表的负载因子过高时,即桶中键值对的数量过多,可能会导致性能下降,因此可能需要进行动态调整,例如重新分配更大的数组并重新插入所有键值对。
C语言中可以使用自定义的结构体来表示哈希表,包含数组和链表等成员变量。
操作哈希表的函数可以实现插入、查找、删除等操作,通过调用这些函数来使用哈希表。
总之,哈希表是一种在C语言中实现键-值对存储和检索的数据结构,通过哈希函数将键映射到数组索引,通过数组和链表的组合解决哈希冲突。
哈希表
哈希表(hashtable)注:哈希表为1.24及以上版本才有的功能,以下版本是无法使用的说~(在1.24之前,游戏缓存(ganecache)+return bug起到了相同的作用,124之后它们即被哈希表取代,并且return bug在1,24之后,被修复了)本演示侧重于hashtable,仅仅会顺带提到hashtable与gamecache两种方式的等价代码转换~☆哈希表的特点与优势~散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。
也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。
这个映射函数叫做散列函数,存放记录的数组叫做散列表。
当然这个概念可能过于深奥,我们不必了解那么深入,只需要了解它的功能以及如何使用~(当然有能力的童鞋,推荐去百度寻找详解)先简单介绍下好了~hashtable就相当于一个存储数据的仓库,其具有容量大以及存储速度稳定的特点~使用hashtable与GetHandleId函数,能够非常轻易地实现一个技能的多人无冲突使用~☆先来认识下这货~首先,我们先来声明一个哈希表对象~由于哈希表通常起到全局范围内的数据存储以及传递~所以我们绝大多数情况(和所有基本没区别)都是将其作为一个全局变量来声明(几乎没有局部变量的哈希表,只有在某些特殊需求下,才会罕见地出现;如果你明确知道自己创建局部hashtable的目的,并且知道如何妥善掌控,那么是毫无问题的)jassglobalshashtable ht=InitHashtable()//函数InitHashtable,无参数,返回一个新建的哈希表对象//在向一个哈希表中存入数据之前,必须先通过此函数创建哈希表,否则无效(好比你无法往一个根本不存在的容器中倒水一样的说~)endglobals很简单,这样就创建了一个哈希表,你可以在地图中的任何地方(没错,任何地方)访问它~Tips:(显式声明globals块(也就是上面)的方式,其实是Vjass才有的功能~如果你的编辑器UI没有这个,请在T的变量管理器中,创建一个哈希表对象,但别忘了加上udg_前缀以及调用InitHashtable函数进行初始化~)然后我们可以试着,在其中存并且读取一些数据~jassfunction Trig_Init_Actions takes nothing returns nothinglocal integer i=5local integer ret//两个整数变量call SaveInteger(ht,0,0,i)//将整数i,存入哈希表ht的0,0号位置~set ret=LoadInteger(ht,0,0)//设置ret=从哈希表ht的0,0号位置读取数据~call BJDebugMsg(I2S(ret))//显示ret的值~endfunction//==============================================function InitTrig_Init takes nothing returns nothing//触发器的初始化函数~//“地图初始化”这个事件比较特殊,没有代码,只需在上方勾上那个勾即可的说~ set gg_trg_Init = CreateTrigger( )call TriggerAddAction( gg_trg_Init, function Trig_Init_Actions )endfunction| || | 测试结果~\ /\ /很好,我们这就完成了一次最简单的读写~输出的结果正确~下面就再详细介绍一下哈希表的相关函数~☆笨蛋的函数详解?真的好⑨呢~接下来简单介绍些哈希表的常用函数~________________________________________________________________________________初始化类:native InitHashtable takes nothing returns hashtable________________________________________________________________________________数据存入类:native SaveInteger takes hashtable table, integer parentKey, integer childKey,integer value returns nothingnative SaveUnitHandle takes hashtable table, integer parentKey, integer childKey,unitwhichUnit returns boolean该类函数均有一样的格式:函数名:Save+数据格式(如Integer、Boolean,都是变量格式首字母大写)+(Handle (Real、Boolean、Integer、String以外格式均要加))如上面SaveInteger即保存整数~SaveUnitHandle即保存单位~特别注意字符串保存函数:SaveStr 名字比较特立独行,单独记忆即可~参数列表:均为4项①(hashtable)你需要保存数据的哈希表对象~②(integer)数据保存的路径主索引(也叫父索引)~③(integer)子索引,同主索引一同构成完整路径~④(类型不一)各类你需要存入的数据~返回值一般无视即可~该类函数还有:SaveReal , SaveBoolean,SaveStr,SaveUnitHandle,SaveGroupHandle,SaveForceHandle,SaveEffectHandle 等等共几十条~如果记不住的话,可以在T中写好,然后转成jass查看的说~注意!一个相同路径(比如0,0)底下,只能存一个Handle类(除integer,boolean,real,string以外;code不算)的数据!(更准确点,是integer, real, boolean, string, handle各一个)SaveInteger(ht,0,0,5)与SaveReal(ht,0,0,3.5)不冲突;SaveUnitHandle(ht,0,0,......)SaveGroupHandle(ht,0,0,......)//虽然类型不同,但仍然将上面那条存的单位覆盖的说~ 并且不可以存入一个null值~比如SaveUnitHandle(ht,0,0,null)这条语句实际并没有任何作用;如果要清空数据请见下方专门函数~________________________________________________________________________________数据读取类:native LoadInteger takes hashtable table, integer parentKey, integer childKeyreturnsintegernative LoadUnitHandle takes hashtable table, integer parentKey, integer childKeyreturnsunit还有很多其他的,就不一一列举了,与上面Save系列函数一一对应~该类函数的格式和Save系列十分相似,Load+数据类型+(Handle)参数列表与上面相比,只是少了一个数据项而已,毕竟现在我们是要读取,而不是保存,只需要路径即可的说~该类函数的返回值类型,就是你需要读取的数据类型~________________________________________________________________________________判断类:HaveSavedBooleanHaveSavedIntegerHaveSavedStrHaveSavedRealHaveSavedHandle该类函数都是三个参数(哈希表,主索引,子索引),用于判断某个位置是否保存了数据~返回值为布尔类型~不过这几个功能并不常用,所以就不过多解释了呢~注意!不要和HaveStoredXXXXX系列的函数混起来,那个是判断缓存中数据的~________________________________________________________________________________单项数据清空(仅常用):RemoveSavedBooleanRemoveSavedIntegerRemoveSavedStrRemoveSavedRealRemoveSavedHandle该类函数,顾名思义,就是清除已经存入表中的某个单项数据(即清空某个主目录+子目录下的一项数据)~参数列表与上面读取类的函数相对应,都是三项~不同的仅仅是上面的函数是读取数据,而本类函数将数据抹杀~凡是一切handle类数据的清除,都使用同一个函数RemoveSavedHandle~________________________________________________________________________________多项数据清空:native FlushChildHashtable takes hashtable table, integer parentKey returnsnothing字面意思清空子哈希表,实际就是清除相同父目录下的数据~jass//以下语句存储的数据如果使用call FlushChildHashtable( ht, 0 )call SaveInteger( ht, 0, 0, 999 )//被清除call SaveInteger( ht, 0, 1, 888 )//被清除call SaveInteger( ht, 0, 2, 777 )//被清除call SaveReal( ht, 0, 3, 5.5 )//被清除call SaveUnitHandle( ht, 0, 4, ...... )//被清除call SaveBoolean( ht, 1, 0, true )//保留call SaveGroupHandle(ht, 1, 1, CreateGroup() )//保留native FlushParentHashtable takes hashtable table returns nothing这个星就不解释了,参数仅仅一个哈希表,猜都能猜到:血洗表中所有数据,并销毁table这个哈希表本身~也就是说,下次再使用某hashtable的话,要重新InitHashtable()创建的说~________________________________________________________________________________映射类(仅一个):native StringHash takes string s returns integer本函数的作用就是将玩家输入的字符串,映射为一串整数(不同字串有着非常巨大的几率不重复,但理论上一定存在两个不同字串映射到同一整数上面的可能性)以迎合哈希表存储时,需要以整数作为路径的需要;使得用户能够以字符的形式,表示路径~jasscall SaveInteger( ht, 0, StringHash("Value"), 999 )几点注意:①该函数不区分字符大小写:aaa,AAA,aAa被认为相同~②该函数与S2I函数有本质区别~________________________________________________________________________________函数GetHandleId(非常重要):native GetHandleId takes handle h returns integer或许看了那么久的教程,人会感觉非常困,但是现在无论如何请打起精神来~本函数可以说是整个哈希表体系中,最重要的东西之一~它的作用,就是获取一个句柄类型对象的ID~你可以理解为,所有单位、单位组、玩家、玩家组、特效、闪电、触发、可破坏物......它们都属于句柄,并且每一个都有自己独一无二(没有错,独一无二)的整数编号,即HandleId~本函数无可替代的作用,就是获取句柄对象的ID~熟悉1.20的使用者可以简单的将这个函数,认为是和H2I完全一样的等价物~用法很简单的说~如下:jasslocal unit u=GetTriggerUnit()local effect e=AddSpecialEffect( ...... )local integer id_1=GetHandleId( u )local integer id_2=GetHandleId( e )//说明一下,GetHandleId函数需求的,是一个handle类的数据//但是你可以随便放入unit, location, effect之类的类型//因为它们实际上继承于handle关于这个函数扮演何种重要角色,将在下面详细解说________________________________________☆关于存储顺序~唔......可以看到我们刚才,是将计时器的HandleId作为存储数据的主路径~有人可能会说,如果作为子路径可不可以,比如SaveInteger(ht, 0, id, 999) 这样~当然这样做本身并无问题,但是并不赞成,会使事情变得麻烦的说~jasslocal timer t=CreateTimer()local integer id=GetHandleId(t)call SaveInteger(ht, id, 0, 9)call SaveInteger(ht, id, 1, 12)call SaveReal (ht, id, 2, 4)call SaveBoolean(ht, id, 3, true)call FlushChildHashtable(ht,id)//这样就可以清空上面四条(或更多主路径相同)存储的数据~call SaveInteger(ht, 0, id, 9)call SaveInteger(ht, 1, id, 12)call SaveReal (ht, 2, id, 4)call SaveBoolean(ht, 3, id, true)call RemoveSavedInteger(ht, 0, id)call RemoveSavedInteger(ht, 1, id)call RemoveSavedInteger(ht, 2, id)call RemoveSavedInteger(ht, 3, id)//同样的效果,四个数据也被清除了,但是每条存储都必须单独对应一条清除,非常麻烦~//相比主路径相同,这样颠倒后更加不易于管理~//“绑定”的思想也不是那么明确了的说~☆一点小知识喵~jass//两点(x0,y0),(x1,y1)间距~local real distset dist=SquareRoot(Pow(x0-x1,2.0)+Pow(y0-y1,2.0))//两点A(x0,y0),B(x1,y1)间方向~local real angset ang=Atan2(y1-y0, x1-x0)//注意这里是A到B的方向~//如果写成set ang=Atan2(y0-y1, x0-x1)//就成了B到A的方向的说~//极坐标位移~//从起始点(x,y),向a方向位移d距离~set newx = x + d*Cos(a)set newy = y + d*Sin(a)//还有一条喵~使用三角函数时,请注意角度制和弧度制的说~☆热身喵~写一个具有延时杀死单位的功能的函数~要求支持多人~很容易就想到用计时器呢~这次我们可以试着将数据与计时器绑定起来,从而实现支持多人的说......很简单的一个小函数呢~很新的新手也完全可以自己试着写写看哦~利用上面谈到的GetHandleId的特性~jassglobalsconstant hashtable ht=InitHashtable()endglobalsfunction TimerKillUnitCallBack takes nothing returns nothinglocal timer t=GetExpiredTimer()local integer id=GetHandleId(t)//这里并不会产生冲突~//虽然有多个计时器到期触发该函数~//但是它们的【ID都一一不同】~//而对应的单位是按照各自ID作为路径存储的~//所以每次获取的都是各自的单位,互不冲突的说~local unit u=LoadUnitHandle(ht,id,0)if (u!=null) thencall KillUnit(u)endifcall DestroyTimer(t)call FlushChildHashtable(ht,id)//清除哈希表中的数据,也是排泄的一部分,初学者容易遗漏~set t=nullset u=nullendfunctionfunction TimerKillUnit takes unit u,real time returns nothinglocal timer t=CreateTimer()local integer id=GetHandleId(t)//计时器的HandleId~call SaveUnitHandle(ht,id,0,u) //将计时器的HandleId作为路径,存储单位~call TimerStart(t,time,false,function TimerKillUnitCallBack)set t=nullendfunction怎么样?是不是很简单~hashtable和GetHandleId的配合使用,可以十分轻松地使技能能够支持多人无冲突~这也素jass技能的魅力之一~★技能实例~一个简单击退~( 支持多人)于是来个简单的喵~所以素被人做烂了的击退呢的说~不过用来上手hashtable应该还素不错的呢~思路很简单的说~ 单位被攻击->创建计时器->不断朝一个方向移动单位~所以直接看代码吧~只素击退而已的说~.w3x (17.54 KB)jass//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//// 【请从最底下开始阅读的说~】////globalsconstant hashtable ht=InitHashtable()endglobalsfunction funcB takes nothing returns nothinglocal timer t=GetExpiredTimer()local integer id=GetHandleId(t)local integer cnt=LoadInteger(ht,id,0)//获取计时器、ID、执行次数~local unit ulocal real xlocal real yset cnt=cnt+1call SaveInteger(ht,id,0,cnt)//执行次数增加一次~set u=LoadUnitHandle(ht,id,1)set x=GetUnitX(u)+LoadReal(ht,id,2)set y=GetUnitY(u)+LoadReal(ht,id,3)call SetUnitX(u,x)call SetUnitY(u,y)//移动单位~if (cnt-cnt/5*5==0) then//这行即cnt mod 5=0时~也就是cnt除以5余数为0~callDestroyEffect(AddSpecialEffect("Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl",x,y))endifif (cnt>=30) then//到达执行次数上限后销毁计时器,清除哈希表中数据的说~call DestroyTimer(t)call FlushChildHashtable(ht,id)endifset u=nullset t=nullendfunctionfunction funcA takes nothing returns nothinglocal unit attackerlocal unit targetlocal timer tlocal integer idlocal real angset attacker=GetAttacker()set target=GetTriggerUnit()//获取攻击者和被攻击单位~set t=CreateTimer()set id=GetHandleId(t)//计时器及其句柄ID~set ang=Atan2(GetUnitY(target)-GetUnitY(attacker),GetUnitX(target)-GetUnitX(attacker)) //获取攻击者到被攻击单位的方向~call SaveInteger(ht,id,0,0)//用于记录计时器执行函数的次数~call SaveUnitHandle(ht,id,1,target)call SaveReal(ht,id,2,15.0*Cos(ang))call SaveReal(ht,id,3,15.0*Sin(ang))//单位XY轴方向上的移动速率~call TimerStart(t,.02,true,function funcB)set attacker=nullset target=nullset t=null//局部变量排泄~endfunctionfunction InitTrig_KnockBack takes nothing returns nothinglocal trigger t=CreateTrigger()call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_ATTACKED) call TriggerAddAction(t,function funcA)set t=null//触发器的初始化函数~第一篇教程里面貌似介绍过了的说~ endfunction★技能实例~幻符「杀人木偶」(进阶)唔......可怜的门番美玲的说~话说这技能果断大家都说太难了呐...没什么算法,但是比较考jass底力,供进阶者参考的说~思路喵~于是这个技能主要是用一些相同运动模式的小刀组合而成的说~ 绕自身旋转着发射小刀~每把小刀先是向外运动一段距离~然后对随机敌人发射的说~没有敌人则随机散射~(稍微改了下原作的设定,原作的判定更复杂些呢,还有旋转~或者......其实星只素在偷懒的说?)幻符「杀人木偶」by wow8 夜の星.w3x (356.25 KB) 唔.......代码也贴出来好了~jass//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~////// 【请从最底下往上阅读的说~】////globalsconstant hashtable ht=InitHashtable()endglobals//全局的哈希表喵~不解释了哦~function funcD takes nothing returns nothing//计时器周期性执行的函数,控制小刀运动轨迹的说~local timer t=GetExpiredTimer()local integer id=GetHandleId(t)local unit u=LoadUnitHandle(ht,id,1)local real xlocal real ylocal integer cnt=LoadInteger(ht,id,0)local boolean endlocal group glocal unit targetlocal unit selectedlocal integer nlocal player plocal real aset cnt=cnt+1call SaveInteger(ht,id,0,cnt)//首先读取各类数据,声明变量等等的说~set end=(cnt>=100)//end变量用于标识小刀是否该结束运动~//因为有多个结束运动的条件~所以用一个变量处理较为方便的说~if (cnt<=50) then//cnt∈[1,50],运动的第一阶段,小刀按扇形轨迹向外扩散的说~set x=GetUnitX(u)+LoadReal(ht,id,2)*(75.0-cnt)/75.0set y=GetUnitY(u)+LoadReal(ht,id,3)*(75.0-cnt)/75.0call SetUnitX(u,x)call SetUnitY(u,y)//移动单位的说~只素个匀减速运动而已,不懂的童鞋们要补习物理了哦~ if (cnt==50) then//小刀发射前最后一瞬间喵~要锁定一个敌人然后飞过去的说~set n=0 //符合条件的单位计数~后面要用的说~ set selected=null //用于存储单位组中随机被选中单位~set p=GetOwningPlayer(u)set g=CreateGroup()call GroupEnumUnitsInRange(g,x,y,1500.0,null)//单位组选取的说~不懂的童鞋请复习下上篇教程的说~loop//循环遍历单位组中所有单位~同样上篇介绍过了的说~set target=FirstOfGroup(g)exitwhen (target==null)if (IsUnitEnemy(target,p) and notIsUnitType(target,UNIT_TYPE_DEAD) and notIsUnitType(target,UNIT_TYPE_FLYING)) thenset n=n+1if (GetRandomInt(1,n)==1) thenset selected=targetendif//第一个单位,100%几率替换之前单位~//第二个单位,50%几率替换~//第三个单位,33%几率替换~// ......//第N个单位,(100/N)%几率替换~//这样的话,每一个单位最后被选中的概率都素均等的说,数学证明略~ endifcall GroupRemoveUnit(g,target)endloopcall DestroyGroup(g)if (selected!=null) then//如果周围有符合条件的单位,则角度面向该单位~set a=Atan2(GetUnitY(selected)-y,GetUnitX(selected)-x)else//如果没有的话,就随便乱飞好了>.<set a=GetRandomReal(0.0,6.283185)endifcall RemoveUnit(u)callSaveUnitHandle(ht,id,1,CreateUnit(p,'h000',x,y,a*bj_RADTODEG))//重新创建一个小刀,以实现瞬间转向~call SaveReal(ht,id,2,50.0*Cos(a))call SaveReal(ht,id,3,50.0*Sin(a))//重新设定下移动速率的说~endifelse//这里就是发射后小刀的运动了喵~set p=GetOwningPlayer(u)set x=GetUnitX(u)+LoadReal(ht,id,2)set y=GetUnitY(u)+LoadReal(ht,id,3)call SetUnitX(u,x)call SetUnitY(u,y)set g=CreateGroup()//选取敌人造成伤害~call GroupEnumUnitsInRange(g,x,y,75.0,null)loopset target=FirstOfGroup(g)exitwhen (target==null)if (IsUnitEnemy(target,p) and notIsUnitType(target,UNIT_TYPE_DEAD) andnotIsUnitType(target,UNIT_TYPE_FLYING)) thenset end=true//打到人了也停止运动的说~callDestroyEffect(AddSpecialEffect("Abilities\\Weapons\\BallistaMissile\\BallistaImpact.mdl",x,y))callUnitDamageTarget(u,target,15.0,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,WEAPON_TYPE_W HOKNOWS)exitwhen trueendifcall GroupRemoveUnit(g,target)endloopcall DestroyGroup(g)endifif (end) then//于是结束了的话,要删掉小刀,销毁计时器并且清除哈希表中数据的说~call PauseTimer(t)call DestroyTimer(t)call FlushChildHashtable(ht,id)call KillUnit(u)endifset t=nullset u=nullset g=nullset target=nullset p=nullset selected=nullendfunctionfunction funcC takes player p,real x,real y,real a returns nothing//喵~于是这里不解释了哦,下面已经说过了呢~local timer t=CreateTimer()local integer id=GetHandleId(t)local unit u//这里的计时器用以控制一把小刀的说~call SaveInteger(ht,id,0,0)set u=CreateUnit(p,'h000',x,y,a*bj_RADTODEG)call SaveUnitHandle(ht,id,1,u)call SaveReal(ht,id,2,10.0*Cos(a))call SaveReal(ht,id,3,10.0*Sin(a))//小刀因为是直线运动,所以可以先行算好X轴,Y轴的速率然后分别存储的说~ call TimerStart(t,0.02,true,function funcD)set t=nullset u=nullendfunctionfunction funcB takes nothing returns nothing//这个函数就是用于创建环形小刀的~//由计时器每隔一段时间到期后调用~local timer t=GetExpiredTimer()//到期的计时器~local integer id=GetHandleId(t)//获取计时器句柄号~local integer cnt=LoadInteger(ht,id,0)//我们一开始保存的执行次数,用于累加~local unit ulocal real alocal integer ilocal player plocal real xlocal real yset cnt=cnt+1call SaveInteger(ht,id,0,cnt)//每次执行次数+1并且保存的说~set u=LoadUnitHandle(ht,id,1)set a=LoadReal(ht,id,2)+cnt*0.045//从哈希表中继续读入数据~set p=GetOwningPlayer(u)set x=GetUnitX(u)set y=GetUnitY(u)set i=0loopcall funcC(p,x,y,a+3.14159*i)//funcC函数是有参数的呢...依次是玩家,X坐标,y坐标以及方向~//方向为弧度制~( π相当于180°)//这里循环两次,角度累加180,相当于是创建两个反向飞行的小刀~set i=i+1exitwhen (i>1)endloopif (cnt>=40) thencall PauseTimer(t)call DestroyTimer(t)call FlushChildHashtable(ht,id)endif//执行次数达到上限后,销毁计时器~//别忘了清空哈希表中数据哦~set t=nullset u=nullset p=nullendfunctionfunction funcA takes nothing returns nothinglocal timer tlocal integer idlocal unit u//局部变量声明~//必须函数开头,以后就不再讲了的说~if (GetSpellAbilityId()!='A000') thenreturnendif//这次的触发器星没有写条件函数~//反正放在动作里面判断一样的说~//如果不是A000,则return,退出该函数~//没有返回值的函数,return什么都不加即可退出的说~set t=CreateTimer()set id=GetHandleId(t)//创建计时器,获取句柄号~//这个计时器用于创建环形的小刀~set u=GetTriggerUnit()call SaveInteger(ht,id,0,0)call SaveUnitHandle(ht,id,1,u)call SaveReal(ht,id,2,(GetUnitFacing(u)+90.0)*bj_DEGTORAD)//这里就是将执行次数,触发单位(施法者),以及角度存入哈希表~ //下一个函数中将会用到的说~//bj_DEGTORAD:其值等于π(圆周率)/180.0//用以将角度制转换为弧度制~call TimerStart(t,0.04,true,function funcB)//TimerStart应该不用讲了喵~//不懂的童鞋请翻阅上篇教程的说~set t=nullset u=null//handle型局部变量必须set null排泄~endfunctionfunction InitTrig_TAT takes nothing returns nothinglocal trigger t=CreateTrigger() callTriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT) //为触发器注册任意单位事件~//EVENT_PLAYER_UNIT_SPELL_EFFECT 即玩家单位发动技能效果~//任意单位事件其实是若干个玩家单位事件组合实现的~//嘛,如果不理解也没关系,知道它的功能就行了的说~call TriggerAddAction(t,function funcA)//为触发器注册动作~set t=nullendfunction* 技能实例~Lightning Ufo~(喂~没搞错吧?)>.<确实是老物了喵~自己学jass后没多久写的失败作呢的说~(不过作为教学资料还有点利用价值的说~笑~)复杂度倒是有点呢~不过星觉得,放在这个教程里面还素挺合适的说~(才没想偷懒呢的说!)嘛,纯粹是过分的jass基本功+熟练度考察,能看懂或者会写这个基本上就已经是相对熟练的状态了~其实jass技能没什么难的,这样的东西,其实跟着教程学过来的话,素可以看明白甚至自己写的哦~有能力的童鞋们可以试试看去理解下的说LightningUFO.w3x (32.96 KB)于是本篇米有了哦~。
哈希表——线性探测法、链地址法、查找成功、查找不成功的平均长度
哈希表——线性探测法、链地址法、查找成功、查找不成功的平均长度⼀、哈希表1、概念哈希表(Hash Table)也叫散列表,是根据关键码值(Key Value)⽽直接进⾏访问的数据结构。
它通过把关键码值映射到哈希表中的⼀个位置来访问记录,以加快查找的速度。
这个映射函数就做散列函数,存放记录的数组叫做散列表。
2、散列存储的基本思路以数据中每个元素的关键字K为⾃变量,通过散列函数H(k)计算出函数值,以该函数值作为⼀块连续存储空间的的单元地址,将该元素存储到函数值对应的单元中。
3、哈希表查找的时间复杂度哈希表存储的是键值对,其查找的时间复杂度与元素数量多少⽆关,哈希表在查找元素时是通过计算哈希码值来定位元素的位置从⽽直接访问元素的,因此,哈希表查找的时间复杂度为O(1)。
⼆、常⽤的哈希函数1. 直接寻址法取关键字或者关键字的某个线性函数值作为哈希地址,即H(Key)=Key或者H(Key)=a*Key+b(a,b为整数),这种散列函数也叫做⾃⾝函数.如果H(Key)的哈希地址上已经有值了,那么就往下⼀个位置找,知道找到H(Key)的位置没有值了就把元素放进去.2. 数字分析法分析⼀组数据,⽐如⼀组员⼯的出⽣年⽉,这时我们发现出⽣年⽉的前⼏位数字⼀般都相同,因此,出现冲突的概率就会很⼤,但是我们发现年⽉⽇的后⼏位表⽰⽉份和具体⽇期的数字差别很⼤,如果利⽤后⾯的⼏位数字来构造散列地址,则冲突的⼏率则会明显降低.因此数字分析法就是找出数字的规律,尽可能利⽤这些数据来构造冲突⼏率较低的散列地址.3. 平⽅取中法取关键字平⽅后的中间⼏位作为散列地址.⼀个数的平⽅值的中间⼏位和数的每⼀位都有关。
因此,有平⽅取中法得到的哈希地址同关键字的每⼀位都有关,是的哈希地址具有较好的分散性。
该⽅法适⽤于关键字中的每⼀位取值都不够分散或者较分散的位数⼩于哈希地址所需要的位数的情况。
4. 折叠法折叠法即将关键字分割成位数相同的⼏部分,最后⼀部分位数可以不同,然后取这⼏部分的叠加和(注意:叠加和时去除进位)作为散列地址.数位叠加可以有移位叠加和间界叠加两种⽅法.移位叠加是将分割后的每⼀部分的最低位对齐,然后相加;间界叠加是从⼀端向另⼀端沿分割界来回折叠,然后对齐相加.5. 随机数法选择⼀个随机数,去关键字的随机值作为散列地址,通常⽤于关键字长度不同的场合.6. 除留余数法取关键字被某个不⼤于散列表表长m的数p除后所得的余数为散列地址.即H(Key)=Key MOD p,p<=m.不仅可以对关键字直接取模,也可在折叠、平⽅取中等运算之后取模。
hash表 装填因子
hash表装填因子哈希表,又称散列表,是一种常用的数据结构,用于存储键值对。
在哈希表中,键通过哈希函数转化为索引,然后将值存储在对应的索引位置上。
装填因子是指哈希表中已存储的键值对数量与哈希表长度的比值,它能够反映哈希表的空间利用率和性能。
一、装填因子的定义和作用装填因子可以用以下公式表示:装填因子 = 已存储的键值对数量 / 哈希表长度装填因子是衡量哈希表使用情况的重要指标,它能够直接影响哈希表的性能。
当装填因子过高时,哈希冲突的概率将增加,查找、插入和删除操作的效率会降低;而当装填因子过低时,哈希表的空间利用率将下降,会浪费大量的存储空间。
因此,选择合适的装填因子是设计和使用哈希表时需要考虑的重要问题。
二、装填因子的确定在实际应用中,装填因子的取值范围通常在0.5到0.8之间。
具体取值应根据实际需求和系统性能进行调整。
如果哈希表需要频繁进行插入和删除操作,为了保证较高的操作效率,可以选择较低的装填因子。
而如果更注重空间利用率,可以选择较高的装填因子。
三、装填因子的影响1. 哈希冲突:当装填因子过高时,哈希冲突的概率会增加。
哈希冲突指的是两个不同的键经过哈希函数计算后得到相同的索引位置。
解决哈希冲突的方法有开放寻址法和链表法等。
2. 性能:装填因子过高时,哈希表的性能将下降。
查找、插入和删除操作的平均时间复杂度将增加。
而当装填因子较低时,哈希表的性能将提高,操作效率更高。
3. 空间利用率:装填因子过低时,哈希表的空间利用率将下降。
大量的存储空间将被浪费。
因此,在追求较高性能的前提下,要尽量提高哈希表的空间利用率。
四、装填因子的调整当哈希表中的键值对数量增加时,装填因子会自动增加。
为了保持哈希表的性能,可以采取以下策略来调整装填因子:1. 动态调整哈希表长度:当装填因子超过一定阈值时,可以通过扩容哈希表的方式来调整装填因子。
扩容时,需要重新计算键的哈希值,并将键值对重新散列到新的哈希表中。
2. 优化哈希函数:选择合适的哈希函数能够减少哈希冲突的概率,提高哈希表的性能。
哈希表是按( )存储方式构造的存储结构
哈希表是按( )存储方式构造的存储结构哈希表是一种按照特定规则构造的存储结构,它的设计灵感来源于散列表。
在哈希表中,数据元素的存储位置是根据元素的键值直接进行计算而得到的,这样可以快速地进行数据的存储和检索。
哈希表的构造方式使得它在处理大量数据时具有很高的效率和性能优势。
哈希表的存储方式是按照键值对的形式进行存储的。
每个数据元素都有一个对应的键值,通过这个键值可以唯一地确定数据元素在哈希表中的存储位置。
这种存储方式保证了数据的快速检索和访问,使得哈希表在大规模数据处理中能够高效地运行。
哈希表采用了哈希函数来计算数据元素的存储位置。
哈希函数是一种将任意长度的输入映射为固定长度输出的函数,它能够将数据元素的键值转化为哈希表中的索引位置。
通过合理设计的哈希函数,可以最大程度地减少数据冲突,提高哈希表的性能和存储效率。
在构造哈希表的存储结构时,需要考虑到哈希函数的设计和冲突处理。
合适的哈希函数能够均匀地分布数据元素,减少冲突的发生;而在冲突处理方面,常见的方法有开放定址法和链地址法等。
这些技术都是为了保证哈希表的高效性和稳定性,使得数据能够在哈希表中得到有效管理和存储。
哈希表在实际应用中有着广泛的应用。
例如,在数据库系统中,哈希表可以用来加速数据的查询和索引操作;在网络通信中,哈希表可以用来实现路由选择和负载均衡等功能。
因此,了解和掌握哈希表的构造方式和存储结构对于提高数据处理效率和系统性能都具有重要意义。
总的来说,哈希表是一种按照特定规则构造的存储结构,它通过哈希函数将数据元素的键值映射为存储位置,实现了快速的数据访问和检索。
哈希表的设计和构造方式使得它在处理大规模数据时具有很高的效率和性能优势,是现代计算机系统中不可或缺的重要数据结构之一。
通过深入理解哈希表的原理和应用,可以更好地优化系统设计和数据管理,提高计算机系统的整体性能和稳定性。
c实现的hash表-概述说明以及解释
c实现的hash表-概述说明以及解释1.引言1.1 概述在计算机科学中,哈希表(Hash Table),又被称为散列表,是一种常用的数据结构。
它能够以常数时间复杂度(O(1))来实现插入、删除和查找等操作,因此具有高效的特性。
哈希表通过哈希函数将键(key)映射到一个固定大小的数组(通常称为哈希表)。
通过这种映射关系,我们可以在数组中快速访问到对应的值(value)。
常见的应用场景包括缓存系统、数据库索引、编译器符号表等。
相对于其他数据结构,哈希表具有以下优点:1. 高效的插入、删除和查找操作:哈希表在插入、删除和查找数据时以常数时间复杂度进行操作,无论数据量大小,都能快速地完成操作。
2. 高效的存储和检索:通过哈希函数的映射关系,哈希表能够将键值对存储在数组中,可以通过键快速地找到对应的值。
3. 空间效率高:哈希表通过哈希函数将键映射到数组下标,能够充分利用存储空间,避免冗余的存储。
然而,哈希表也存在一些局限性:1. 冲突问题:由于哈希函数的映射关系是将多个键映射到同一个数组下标上,可能会导致冲突。
解决冲突问题的常见方法包括链地址法(Chaining)和开放定址法(Open Addressing)等。
2. 内存消耗:由于哈希表需要维护额外的空间来存储映射关系,所以相比于其他数据结构来说,可能会占用较多的内存。
本篇长文将重点介绍C语言实现哈希表的方法。
我们将首先讨论哈希表的定义和实现原理,然后详细介绍在C语言中如何实现一个高效的哈希表。
最后,我们将总结哈希表的优势,对比其他数据结构,并展望哈希表在未来的发展前景。
通过本文的学习,读者将能够深入理解哈希表的底层实现原理,并学会如何在C语言中利用哈希表解决实际问题。
1.2 文章结构本文将围绕C语言实现的hash表展开讨论,并按照以下结构进行组织。
引言部分将对hash表进行概述,介绍hash表的基本概念、作用以及其在实际应用中的重要性。
同时,引言部分还会阐述本文的目的,即通过C语言实现的hash表,来探讨其实现原理、方法以及与其他数据结构的对比。
哈希表的原理
哈希表的原理
哈希表是一种基于哈希函数实现的数据结构,它通过将关键字映射到表中一个位置来访问记录,以加快查找的速度。
哈希表的核心是哈希函数,它将关键字映射到表中一个位置,从而实现快速访问记录的目的。
哈希表的插入、删除和查找操作的时间复杂度都为O(1),这是哈希表的优势所在。
但是,哈希表的性能取决于哈希函数的选择和哈希表的大小。
如果哈希函数选择不当或者哈希表的大小不合适,就会导致哈希冲突,从而影响哈希表的性能。
为了解决哈希冲突的问题,常用的方法是使用开放地址法或者链地址法。
开放地址法是指当哈希冲突发生时,继续在哈希表中寻找下一个空闲位置,直到找到合适的位置为止。
链地址法是指将哈希表中每个位置上的元素都存储为一个链表,当哈希冲突发生时,将新元素插入到相应位置的链表中。
在实际应用中,哈希表广泛应用于各种场景中,如数据库索引、缓存、编译器等。
因为它具有快速查找的优势,可以提高系统的效率。
但是,在使用哈希表时需要注意哈希函数的选择、哈希表的大小以及解决哈希冲突的方法等问题,以充分发挥哈希表的性能优势。
c++的hash表使用方法
c++的hash表使用方法【实用版3篇】篇1 目录1.C++中哈希表的基本概念2.C++中哈希表的使用方法3.哈希表的优缺点篇1正文一、C++中哈希表的基本概念哈希表(HashTable,也叫散列表)是一种基于数组实现的数据结构,通过哈希函数将键映射到数组的一个位置,从而实现快速插入和查询。
哈希表的特点是存储密度高、访问速度快,但是插入和删除操作较慢。
在 C++中,可以使用 std::unordered_map 和 std::unordered_set 来实现哈希表。
它们分别对应于无序的哈希表和有序的哈希表。
二、C++中哈希表的使用方法1.包含头文件要使用哈希表,首先需要包含相应的头文件。
对于无序的哈希表,需要包含<unordered_map>;对于有序的哈希表,需要包含<unordered_set>。
2.创建哈希表使用哈希表前,需要先创建一个哈希表实例。
对于无序的哈希表,可以使用 std::unordered_map<key_type, data_type>;对于有序的哈希表,可以使用 std::unordered_set<key_type>。
其中,key_type 是键的类型,data_type 是值的类型。
3.插入元素使用哈希表,可以通过 insert() 成员函数插入键值对。
无序哈希表使用 insert(const key_type&, const data_type&) 插入元素;有序哈希表使用 insert(const key_type&) 插入元素。
例如:```cppstd::unordered_map<int, std::string> my_map;my_map.insert(1, "one");my_map.insert(2, "two");```4.查询元素使用哈希表,可以通过 count() 成员函数查询元素的个数;通过find() 成员函数查找指定元素是否存在。
哈希表的特征和原理
哈希表的特征和原理哈希表也叫散列表,是⼀种神奇的结构,最⼤的特点就是快。
它的结构有很多种,最流⾏、最容易理解的是:顺序表+链表的结构。
主结构是长度可以动态变化的顺序表,每个顺序表的节点可以单独引出⼀个链表。
哈希表的原理可以从以下三点阐述。
添加数据原理:1)、计算哈希码,调⽤hashCode()⽅法,结果是⼀个int值,整数的哈希码取⾃⾝即可2)、根据哈希码计算存储位置(数组的索引)【y = k(x) (除留取余法)存⼊哈希表】3)、将数据存⼊指定位置,如果已经有元素存在,就是出现了冲突,需要沿着链表⽐较,有重复的元素,不存储。
没有,就存储。
结论:添加快。
时间复杂度O(1);⽆序。
查询数据的原理:和添加过程⼀样,还是三步搞定。
结论:查询快。
总结:哈希表的神奇之处在于按照内容查询,理想情况下可以达到数组索引查询的时间复杂度O(1)。
核⼼在于其查询不是基于⽐较的,⽽是基于计算的。
当存在冲突时,会降低效率。
如何减少冲突:1)装填因⼦:哈希表的长度和表中的记录数的⽐例。
超过装填因⼦就要扩容。
不然冲突的概率会⼤⼤提⾼,从⽽影响性能。
2)哈希函数的选择直接定址法平⽅取中发折叠法除留取余法等3)处理冲突的⽅法链地址法开放地址法再散列法建⽴⼀个公共溢出区hashCode和equals()在哈希表添加查询中的作⽤:1)hashCode():计算哈希码,是⼀个整数,根据哈希码可以计算出数据在哈希表中的存储位置。
2)equals():添加时出现了冲突,需要通过equals进⾏⽐较,判断是否相同,查询时也需要使⽤equals进⾏⽐较,判断是否相同。
数据结构.第9章.查找.4.哈希表
§9.3 哈希表
开放地址法
例:关键码集为 {47,7,29,11,16,92,22,8,3}, 设:哈希表表长为m=11; 哈希函数为Hash(key)=key mod 11; 拟用线性探测法处理冲突。建哈希表: 0 1
11 22
2
3
4
5
6
3
7
7
8
29
9
8
10
47 92 16
§9.3 哈希表
开放地址法
选用关键字的某几位组合成哈希地址。
选用原则应当是:各种符号在该位上出现的频率大致
相同。
适于关键字位数比哈希地址位数大,且可能出现的关 键字事先知道的情况。
§9.3 哈希表
数字分析法
例:有一组(例如80个)关键码,其样式如下: 讨论: 3 4 7 0 5 2 4 ① 第1、2位均是“3和4”,第3位也只有 3 4 9 1 4 8 7 3 4 8 2 6 9 6 “ 7、8、9”,因此,这几位不能用,余 3 4 8 5 2 7 0 下四位分布较均匀,可作为哈希地址选用。 3 4 8 6 3 0 5 ② 若哈希地址取两位(因元素仅80个), 3 4 9 8 0 5 8 则可取这四位中的任意两位组合成哈希地 3 4 7 9 6 7 1 址,也可以取其中两位与其它两位叠加求 3 4 7 3 9 1 9 和后,取低两位作哈希地址。 位号:① ② ③ ④ ⑤ ⑥ ⑦
拟用二次探测法处理冲突。建哈希表如下: Hi = ( H(K)+di ) mod m 其中di =12, -12, 22,-22,…, j2, -j2 ( j≤m/2)。
0 1
11 22
2
3
3
4
5
6
7
哈希表存储效率和装填因子
哈希表存储效率和装填因子哈希表(Hash Table),也称为散列表,是一种常见的数据结构,用于存储键值对(key-value)的数据集合。
它通过将键映射到一个数字索引来实现高效的数据访问。
在哈希表中,每个数字索引都称为“哈希值”,而通过哈希函数计算得出哈希值。
相比于其他数据结构,哈希表具有快速的查找和插入操作的优势。
在理想情况下,哈希表可以在常数时间O(1)内完成这些操作。
然而,在实际应用中,哈希表的存储效率和装填因子是需要考虑的重要问题。
哈希表的存储效率可以通过两个方面来衡量,即空间复杂度和时间复杂度。
首先来看空间复杂度。
在使用哈希表时,需要用到一个数组来存储数据,数组的大小决定了哈希表可以存储的元素数量。
通常情况下,为了提高存储效率,数组的大小会大于等于哈希表实际的元素数量。
当哈希表的元素数量较少时,数组中会存在大量未使用的空间,造成了空间的浪费。
而当哈希表的元素数量增加时,数组中的空间利用率会提高。
因此,可以说哈希表的存储效率与元素数量相关,元素越多,存储效率越高。
而时间复杂度则是度量哈希表存储效率的另一个重要指标。
在理想情况下,哈希表的查找和插入操作都可以在常数时间O(1)内完成。
这是因为哈希函数将键映射到数组中的一个位置,在这个位置上直接存储或查找相应的元素,所需的比较次数非常少。
然而,当哈希函数存在冲突时,即不同的键映射到了相同的位置,就会出现冲突。
解决冲突的常见方法有拉链法(Chaining)和开放地址法(Open Addressing)。
在拉链法中,每个数组位置上的元素都是一个链表,冲突的元素通过链表来存储。
而开放地址法则是通过探查其他空闲位置来寻找存储的位置。
这些解决冲突的方法会引入一定的额外操作,导致时间复杂度略微增加。
因此,冲突的发生和解决也会对哈希表的存储效率产生一定的影响。
此外,哈希表的装填因子也是一个重要的性能指标。
装填因子是指哈希表中已存储元素的数量与哈希表数组大小的比值。
9.3 哈希表
9.3.3、处理冲突的方法法 1. 开放定址法
为产生冲突的地址 H(key) 求下一个哈希地址,如果该地址还 冲突,则再给出下一个地址,由此得到一个地址序列:
H0, H1, H2, …, Hs 1≤ s≤m-1
其中:H0 = H(key)
Hi = ( H(key) + di ) MOD m i=1, 2, …, s
1
3
条件:表长 m 应为形如 4j+3 的素数
(如: 7, 11, 19, 23, … 等)
Hi = ( H(key) + di ) MOD m, i=1, 2, …, s 3) 随机探测再散列
di 是一组伪随机数列 或者
di=i×H2(key) (又称双散列函数探测)
例如: 关键字集合
{ 19, 01, 23, 14, 55, 68, 11, 82, 36 }
ADT HashTable is
Data
HashTable;
Operations
initiate()
初始化Hash表
hash(key) Hash函数
search(key) 查找key
insert(key) 插入key
delete(key) 删除key
reCreate(size) 重建Hash表, 新表空间大于旧表, 旧表中的元素按新表的Hash函数插入新表中
设定哈希函数 H(key) = key MOD 11 ( 表长=11 )
6/28/2020
1 直接定址法 取关键字或关键字的某个线性函数作哈希地址,
即H(key)=key 或 H(key)=a·key+b(a,b为常数) 特点:直接定址法所得地址集合与关键字集合大小
第九章哈希表
对增量 di 有三种取法:
• 1) • 2) • 3) • 线 平 随 性 方 机 探 探 探 测 测 测 再 再 再 散 散 散 列 列 列 di = c× i 最简单的情况 c=1 di = 12, -12, 22, -22, …, di 是一组伪随机数列 或者 伪随机数列 di=i×H2(key) (又称双散列函数探测 又称双散列函数探测) 又称双散列函数探测
二、构造哈希函数的方法 构造哈希函数的方法
对数字 数字的关键字可有下列构造方法: 数字
1. 直接定址法 2. 数字分析法 3. 平方取中法
4. 折叠法 5. 除留余数法 6. 随机数法
若是非数字关键字 非数字关键字,则需先 需先对其进行 进行 非数字关键字 需先 数字化处理。 数字化处理
1. 直接定址法
注意: 应具有“完备性” 注意:增量 di 应具有“完备性”
即:产生的 Hi 均不相同,且所产生的 s(m-1)个 Hi 值能覆盖 覆盖哈希表中所有 个 覆盖 地址。则要求: ※ 平方探测时的表长 m 必为形如 4j+3 的素数(如: 7, 11, 19, 23, … 等); ※ 随机探测时的 m 和 di 没有公因子。
示例: 示例: 有一个关键码 key = 962148,散列表大小 , m = 25,即 HT[25]。取质数 p= 23。散列函数 , 。 。 hash ( key ) = key % p。则散列地址为 。
hash ( 962148 ) = 962148 % 23 = 12。 12。
6.随机数法 随机数法
例如:为每年招收的 1000 名新生建立
一张查找表,其关键字为学号,其值的 范围为 xx000 ~ xx999 (前两位为年份)。 若以下标为 以下标为000 ~ 999 的顺序表 的顺序表表示之。 以下标为 则查找过程可以简单进行:取给定值 (学号)的后三位,不需要经过比较 不需要经过比较便 不需要经过比较 可直接从顺序表中找到待查关键字。
哈希表的键和值
哈希表的键和值
哈希表是一种数据结构,其键和值可以是任何类型的数据。
键是唯一的,用于标识和查找与之关联的值。
值可以是任何类型的数据,包括整数、浮点数、字符串、布尔值、数组、哈希表等。
在哈希表中,键通过哈希函数被映射到某个位置,这样就可以快速地访问和查找对应的值。
哈希表的平均时间复杂度可以接近O(1),使得它非常高效。
如果所有的键都是整数,那么可以使用一个简单的无序数组来实现哈希表。
将键作为索引,值即为其对应的值,这样就可以快速访问任意键的值。
如果键不是整数,可以将键转换为哈希值,然后将哈希值用作索引来存储对应的值。
需要注意的是,哈希表的一个挑战是如何处理哈希冲突,也就是两个不同的键经过哈希函数计算得到相同的哈希值的情况。
常见的处理哈希冲突的方法有开放定址法、链地址法等。
哈希表的查找
1
2 3
4
2)算法思想: 设n 个记录存放在一个有序顺序表 L 中,并按其关键 码从小到大排好了序。查找范围为l=0, r=n-1; 求区间中间位置mid=(l+r)/2; 比较: L[mid].Key = x,查找成功,返回mid,结束; L[mid].Key > x,r=mid-1; L[mid].Key < x,l=mid+1; 若l<=r 转2,否则查找失败,返回 0;
对查找表常用的操作有哪些?
查询某个“特定的”数据元素是否在表中; 查询某个“特定的”数据元素的各种属性; 在查找表中插入一元素; 从查找表中删除一元素。
9.1 基本概念
如何评估查找方法的优劣? 查找的过程就是将给定的值与文件中各记录的关 键字逐项进行比较的过程。所以用比较次数的平均值 来评估算法的优劣,称为平均查找长度(ASL: average search length)。i 1 i Ci ASL P
考虑对单链表结构如何折半查找? ——无法实现!
2)算法实现:
int Search_Bin ( SSTable ST, KeyType key ) { // 在有序表ST中折半查找其关键字等于key的数据元素。 // 若找到,则函数值为该元素在表中的位置,否则为0。 low = 1; high = ST.length; // 置区间初值 while (low <= high) { mid = (low + high) / 2; if (key == ST.elem[mid].key) return mid; // 找到待查元素 else if ( key < ST.elem[mid].key) high = mid - 1; // 继续在前半区间进行查找 else low = mid + 1; // 继续在后半区间进行查找 } return 0; // 顺序表中不存在待查元素 } // Search_Bin
哈希表的概念理解
哈希表的概念理解1.什么是Hash表?Hash表也称散列表,也有直接称为哈希表,是⼀种根据关键字值(key-value)⽽直接进⾏访问的数据结构。
它是通过把关键字映射到数组的下标来加快查找速度。
普通的数据结构中查找某⼀个关键字通常需要遍历整个数据结构,时间复杂度O(n),⽽哈希表只需要O(1)的时间级。
我们知道个重要的问题就是如何把关键字转换为数组的下标,这个转换的函数称为哈希函数(也称散列函数),转换的过程称为哈希化。
2.介绍哈希函数⼤家都⽤过字典,字典的优点是我们可以通过前⾯的⽬录快速定位到所要查找的单词。
如果我们想把⼀本英⽂字典的每个单词,从 a 到zyzzyva(这是⽜津字典的最后⼀个单词),都写⼊计算机内存,以便快速读写,那么哈希表是个不错的选择。
这⾥我们将范围缩⼩点,⽐如想在内存中存储5000个英⽂单词。
我们可能想到每个单词会占⽤⼀个数组单元,那么数组的⼤⼩是5000,同时可以⽤数组下标存取单词,这样设想很完美,但是数组下标和单词怎么建⽴联系呢? ⾸先我们要建⽴单词和数字(数组下标)的关系: 我们知道 ASCII 是⼀种编码,其中 a 表⽰97,b表⽰98,以此类推,⼀直到122表⽰z,⽽每个单词都是由这26个字母组成,我们可以不⽤ ASCII 编码那么⼤的数字,⾃⼰设计⼀套类似 ASCII的编码,⽐如a表⽰1,b表⽰2,依次类推,z表⽰26,那么表⽰⽅法我们就知道了。
接下来如何把单个字母的数字组合成代表整个单词的数字呢? ①、把数字相加 ⾸先第⼀种简单的⽅法就是把单词的每个字母表⽰的数字相加,得到的和便是数组的下标。
⽐如单词 cats 转换成数字: cats = 3 + 1 + 20 + 19 = 43 那么单词 cats 存储在数组中的下标为43,所有的英⽂单词都可以⽤这个办法转换成数组下标。
但是这个办法真的可⾏吗? 假设我们约定⼀个单词最多有 10 个字母,那么字典的最后⼀个单词为 zzzzzzzzzz ,其转换为数字: zzzzzzzzzz = 26*10 = 260 那么我们可以得到单词编码的范围是从1-260。
哈希表-By 吴航
研究报告:哈希表-By 吴航一.什么是哈希表?定义:哈希表是根据关键码值而直接进行访问的数据结构。
二.算法背景。
1953年,H.P.Luhn首先提出散列表的思想,G.M.Amdahl首次提出开放寻址法的思想。
1979年,Carter和Wegman引入全域散列函数列的概念。
三.哈希表的构造。
1.思路哈希表是一种数据压缩的手段,最终的目的是把一些有着各种各样个关键字的数据(字符,字符串,一些整数……)压缩到一张表中,使我们可以很方便的通过特定的函数(就是散列函数啦),找到表中的对应的数据(而不是一个一个翻过去,空间换时间),因此,问题就成了怎么构建这个函数?2.怎么构建呢?1)直接寻址法顾名思义,就是根据关键字直接寻址。
(H(x)=x)2)数字分析法通过分析题目中的关键字找散列函数。
3)平方取中法取关键字平方后的中间几位为哈希地址。
4)折叠法将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址,这方法称为折叠法。
例:13824769138 247 69H(13824769)=(138+247+69) mod 1000=4545)除留余数法取关键字被某个不大于哈希表表长m的数p除后所得余数为哈希地址。
(H(key)=key MOD p (p<=m)),p最好取大质数。
6)随机数法选择一个随机函数,取关键字的随机函数值为它的哈希地址。
2.冲突了?有的时候,会出现这样的情况——两个关键字递归到同一地址。
怎么办呢?1)开放地址法如果冲突了,显然要是再存在这就覆盖了对吧,因此,我们需要再找一个……建立数组d[i],如果H[i]冲突,就找H[i]=(H[i]+d[i]) mod p(p为哈希表总长度)下一个问题随之而来:d[i]怎么构建?方法A:di = 1,2,3,……线性探测再散列;方法B:di = 12,-12,22,-22,……二次探测再散列;方法C:di =伪随机序列伪随机再散列;(PS:后面的术语有点……)四.效率。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
2、哈希表的构造方法
直接定址法 除余法 基数转换法 平方取中法 折叠法 移位法 随机数法
2、哈希表的构造方法
直接定址法: 直接定址法:取关键字或关键字的某个线性函数 值为哈希地址。 值为哈希地址。 key) 即:H(key)=key 或 H(key)=a*key+b
例如:有一个从1到100岁的人口数字统计表,其中,年龄作为 例如:有一个从 到 岁的人口数字统计表,其中, 岁的人口数字统计表 关键字,哈希函数取关键字自身。 关键字,哈希函数取关键字自身。 地址 年龄 01 1 02 2 ... ... 25 25 26 26 ... 27 27 ... ... ... ... 100 ... ...
3、处理冲突方法
拉链法
例2: 已知一组关键字为 ,14,23,01, : 已知一组关键字为(19, , , , 68,20,84,27,55,11,10,79), 68,20,84,27,55,11,10,79),散列 函数h=key%13 ,用用拉链法解决冲突构造 函数 这组关键字的散列表(哈希表)。 这组关键字的散列表(哈希表)。
1、什么是哈希表
如果我们以学生姓名为关键字,如何建立查找表, 如果我们以学生姓名为关键字,如何建立查找表, 使得根据姓名可以直接找到相应记录呢? 使得根据姓名可以直接找到相应记录呢? 刘丽 刘宏英
姓名中各拼音 首字母 用所有首字编 号值相加 求和
吴军 wj
吴小艳 wxy
李秋梅 陈伟 ... lqm cw ...
成绩二... 成绩二
95
1、什么是哈希表
如果将来要查李秋梅的成绩, 如果将来要查李秋梅的成绩,可以用上述 方法求出该记录所在位置: 方法求出该记录所在位置: 李秋梅:lqm 取表中第42条记 李秋梅:lqm 12+17+13=42 取表中第42条记 录即可。 录即可。 问题: 问题:如果两个同学分别叫 刘丽 刘兰 该如 何处理这两条记录? 何处理这两条记录? 这个问题是哈希表不可避免的, 这个问题是哈希表不可避免的,即冲突现 象:对不同的关键字可能得到同一哈希地 址。
再哈希法
hi=(h(key)+i*h1(key))% 0≤i≤m- //即 hi=(h(key)+i*h1(key))%m 0≤i≤m-1 //即di=i*h1(key)
拉链法 建立一个公共溢出区
3、处理冲突方法
(1)开放地址法(线性探测再散列、二次探测 开放地址法(线性探测再散列、 再散列、 再散列、随机探测再散列) 1: 已知一组关键字为(26,36,41,38, 例1: 已知一组关键字为(26,36,41,38, 44,15,68,12,06,51),散列函数 , , , , , , h=key%p ,p=13.取m=13(存储空间为 取 ( 13).用线性探查法解决冲突构造这组关 ) 用线性探查法解决冲突构造这组关 键字的散列表。 键字的散列表。 若用二次探测再散列法哪? 若用二次探测再散列法哪?
练习
例7:哈希查找中 k个关键字具有同一哈希值, 个关键字具有同一哈希值, 若用线性探测法将这k 若用线性探测法将这k个关键字对应的记录 存入哈希表中,至少要进行( )次探测 次探测。 存入哈希表中,至少要进行( )次探测。 A.k B.k+1 C.k(k+1)/2 D.1+k(k+1)/2
2、哈希表的构造方法
平方取中法: 平方取中法:先通过求关键字的平方值扩 大相近数的差别, 大相近数的差别,然后根据表长度取中间 的几位数作为散列函数值。 的几位数作为散列函数值。又因为一个乘 积的中间几位数和乘数的每一位都相关, 积的中间几位数和乘数的每一位都相关, 所以,因此产生的散列地址较为均匀。 所以,因此产生的散列地址较为均匀。
4、Hash表的查找 Hash表的查找
哈希查找的方法是一种直接计算存储 地址的方法,在查找过程中, 地址的方法,在查找过程中,如果构造哈 希表所选择的哈希函数使得地址分布均匀 的话,几乎无需进行比较, 的话,几乎无需进行比较,就可以得出 找到”或者“找不到”的结论的。 “找到”或者“找不到”的结论的。但由 于在构造哈希函数时难以避免发生冲突, 于在构造哈希函数时难以避免发生冲突, 因此,在考察哈希查找的效率时, 因此,在考察哈希查找的效率时,不但要 考虑查找时所需比较的次数, 考虑查找时所需比较的次数,还需考虑求 取哈希地址所需的时间,显然, 取哈希地址所需的时间,显然,此时仍然 可以用平均查找长度作为评价哈希查找效 可以用平均查找长度作为评价哈希查找效 率的标准 。
人数 3000 2000 ... 1050 ...
2、哈希表的构造方法
除余法: 除余法:以关键码除以表元素总线后得 到的余数为哈希地址。 到的余数为哈希地址。
例:对21、30、11三个数,利用k mod 3的方式,求 、 、 三个数,利用 的方式, 三个数 的方式 他们的哈希地址: 他们的哈希地址: 21 Mod 3=0 30 Mod 3=0 11 Mod 3=2
练习
例5:( 10 , 8 , 17 , 16 , 4 , 7 , 25 , 18 )进行 :( 哈希制表, 哈希制表,地址空间为 0 ~ 9 ,取,p=7,以除留余数 , 法构造哈希函数,线性探查法解决冲突, 法构造哈希函数,线性探查法解决冲突,画出哈希表并 计算查找成功的平均查找长度。 计算查找成功的平均查找长度。 例6:设哈希表长为 14,哈希函数是 : ,哈希函数是H(key)=key%11, 表中已有数据的关键字为15, , , 共四个 共四个, 表中已有数据的关键字为 ,38,61,84共四个,现 要将关键字为49的结点加到表中 的结点加到表中, 要将关键字为 的结点加到表中,用二次探测再散列 法解决冲突,则放入的位置是( ) 法解决冲突,则放入的位置是 A.8 B.3 C.5 D.9 . . . .
线性探查法解决冲突
21 30 11
2、哈希表的构造方法
基数转换法: 基数转换法:将关键码看作是某个基数制 上的整数, 上的整数,然后将其转换为另一基数制上 的数。 的数。
三个数, 例:对21、30、11三个数,进行基数转换法求哈希 、 、 三个数 地址。 地址。 把这三个数看作是八进制,转成十进制为: 、 把这三个数看作是八进制,转成十进制为:17、 24、9,然后分别将其存放在相应的存取空间。 、 ,然后分别将其存放在相应的存取空间。
三个数进行平方取中法求哈希地址: 例:对21、30、11三个数进行平方取中法求哈希地址: 、 、 三个数进行平方取中法求哈希地址 21*21=441 30*30=900 11*11=121 哈希地址分别为 4、0、2 、 、
3、处理冲突方法
开放地址法(线性探测再散列、二次探测再散列、 开放地址法(线性探测再散列、二次探测再散列、 随机探测再散列) hi=(h(key)+i)mod m 其中0≤i≤m-1 hi=( key)+i) 其中0≤i≤mi=1,2,3……m 1,线性探测再散列 i=1,2,3……m-1,线性探测再散列; 线性探测再散列; i=12,-12, 22,-22 ,32,-32 ……±k2,二次探测再散列; ……± 二次探测再散列; i=伪随机数序列,称伪随机探测再散列。 i=伪随机数序列 称伪随机探测再散列。 伪随机数序列,
ll
lhy
24
46
33
72
42
26
...
最小值可能为3 最大值可能为78 可放76个学生 最小值可能为 最大值可能为 ,可放 个学生
成绩一 3 ... 24 25 26 ... 33 ... 42 ... 46 ... 72 ... 78 ... ... 刘丽 ... 陈伟 ... 吴军 ... 李秋梅 ... 刘宏英 ... 吴小艳 ... ... 82
练习
例4:已知一个线性表(38,25,74,63,52, 已知一个线性表(38,25,74,63,52, 48),假定采用散列函数h(key)=key%7计 48),假定采用散列函数h key)=key%7计 ),假定采用散列函数 算散列地址, 算散列地址,并散列存储在散列表 A[0,1……6]中 A[0,1……6]中,若采用线性探测方法解决冲 突,则在该散列表上进行等概率成功查找的 平均查找长度为( 平均查找长度为( ) A. 1.5 B. 1.7 C. 2.0 D. 2.3
Hash
什么是哈希表? 什么是哈希表? Hash函数的构造方法; Hash函数的构造方法; 函数的构造方法 处理冲突方法; 处理冲突方法; Hash表的查找 Hash表的查找。 表的查找。
1、什么是哈希表
Hash表又可称哈希表、散列表、杂凑表。 Hash表又可称哈希表、散列表、杂凑表。 表又可称哈希表 它是一种十分实用的查找技术, 它是一种十分实用的查找技术,具有极高 的查找效率。 的查找效率。 哈希表最常见的例子是以学生学号为关键 字的成绩表, 字的成绩表,1号学生的记录位置在第一 10号学生的记录位置在第10条 号学生的记录位置在第10 条,10号学生的记录位置在第10条...
4、Hash表的查找 Hash表的查找
按照建立哈希表时的哈希函数,根据给定 按照建立哈希表时的哈希函数, 关键字值,直接求出其哈希地址: 关键字值,直接求出其哈希地址: 若该地址中数据元素为空,则查找失败; (1)若该地址中数据元素为空,则查找失败; 若该地址中数据元素不为空, (2) 若该地址中数据元素不为空,且其关键 字值与给定关键字值相等,则查找成功; 字值与给定关键字值相等,则查找成功; 若该地址中数据元素不为空, (3) 若该地址中数据元素不为空,但其关键 字值不等于给定关键字值, 字值不等于给定关键字值,则需按照建立哈 希表时解决冲突的办法,继续在“ 希表时解决冲突的办法,继续在“下一个哈 希地址”中查找,如此深入, 希地址”中查找,如此深入,直至找到或者 某一哈希地址中的元素为空时结束。 某一哈希地址中的元素为空时结束。