哈希表

合集下载

c 语言 哈希表

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表 装填因子

hash表装填因子哈希表,又称散列表,是一种常用的数据结构,用于存储键值对。

在哈希表中,键通过哈希函数转化为索引,然后将值存储在对应的索引位置上。

装填因子是指哈希表中已存储的键值对数量与哈希表长度的比值,它能够反映哈希表的空间利用率和性能。

一、装填因子的定义和作用装填因子可以用以下公式表示:装填因子 = 已存储的键值对数量 / 哈希表长度装填因子是衡量哈希表使用情况的重要指标,它能够直接影响哈希表的性能。

当装填因子过高时,哈希冲突的概率将增加,查找、插入和删除操作的效率会降低;而当装填因子过低时,哈希表的空间利用率将下降,会浪费大量的存储空间。

因此,选择合适的装填因子是设计和使用哈希表时需要考虑的重要问题。

二、装填因子的确定在实际应用中,装填因子的取值范围通常在0.5到0.8之间。

具体取值应根据实际需求和系统性能进行调整。

如果哈希表需要频繁进行插入和删除操作,为了保证较高的操作效率,可以选择较低的装填因子。

而如果更注重空间利用率,可以选择较高的装填因子。

三、装填因子的影响1. 哈希冲突:当装填因子过高时,哈希冲突的概率会增加。

哈希冲突指的是两个不同的键经过哈希函数计算后得到相同的索引位置。

解决哈希冲突的方法有开放寻址法和链表法等。

2. 性能:装填因子过高时,哈希表的性能将下降。

查找、插入和删除操作的平均时间复杂度将增加。

而当装填因子较低时,哈希表的性能将提高,操作效率更高。

3. 空间利用率:装填因子过低时,哈希表的空间利用率将下降。

大量的存储空间将被浪费。

因此,在追求较高性能的前提下,要尽量提高哈希表的空间利用率。

四、装填因子的调整当哈希表中的键值对数量增加时,装填因子会自动增加。

为了保持哈希表的性能,可以采取以下策略来调整装填因子:1. 动态调整哈希表长度:当装填因子超过一定阈值时,可以通过扩容哈希表的方式来调整装填因子。

扩容时,需要重新计算键的哈希值,并将键值对重新散列到新的哈希表中。

2. 优化哈希函数:选择合适的哈希函数能够减少哈希冲突的概率,提高哈希表的性能。

c实现的hash表-概述说明以及解释

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表使用方法

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章.查找.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 哈希表
6/28/2020
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. 数据库索引: 数据库中的索引通常采用哈希表来实现。

通过将索引关键字转化为哈希值,可以快速定位到需要查询的数据记录。

2. 缓存机制: 缓存系统通常使用哈希表来存储缓存数据。

通过将缓存的关键字转化为哈希值,可以快速判断缓存中是否存在目标数据以及进行数据的读取和更新操作。

3. 字典数据结构: 字典数据结构(如Python中的字典)的实现通常也采用哈希表来存储数据。

通过哈希函数将关键字映射到索引,可以快速地进行数据的插入、删除和查找操作。

4. 路由表: 网络路由器中通常使用哈希表来存储路由表信息。

通过哈希函数将目标IP地址映射到索引,可以快速地确定出口接口及下一跳路由器信息。

三、哈希表的优化技巧1. 哈希函数设计: 哈希函数的设计直接影响到哈希表的性能。

一个好的哈希函数应该能够将关键字均匀地映射到索引,避免冲突。

常见的哈希函数包括除留余数法、平方取中法等。

2. 冲突处理: 冲突是指多个关键字经过哈希函数映射后得到相同的索引值。

常见的冲突处理方法有开放定址法和链地址法。

开放定址法是通过探测到空槽位或者其他空的状态来存储冲突的关键字;链地址法是使用链表来存储冲突的关键字,将它们链接到同一个索引对应的链表上。

哈希表的概念理解

哈希表的概念理解

哈希表的概念理解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 吴航

研究报告:哈希表-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. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

6. 随机数法
选择一个随机函数,取关键字的随机函数值
为它的哈希地址,即H(key)=random (key),其中
random为随机函数。
通常,当关键字长度不等时采用此法构造哈
希函数较恰当。
实际工作中需视不同情况采用不同的哈希函数。通
常考虑的因素:
(1)计算哈希函数所需时间(包括硬件指令的因
素 );
H(84)=(6+2) mod 19=8 H(14)=14 mod 13=1 (冲突) H(27)=27 mod 13=1(冲突) H(14)=(1+1) mod 19=2 H(55)=55 mod 13=3 H(20)=20 mod 13=7 H(27)=(1+1) mod 19=2 (冲突) H(27)=(1+2) mod 19=3 (冲突) H(27)=(1+3) mod 19=4 ……
找单链表中第一个结点的次数为1,第二个结点
次数为2,其余依次类推。
平均查找长度: ASL=1+α/2
例:给定关键字序列 {11,78,10,1,3,2,4,
21},试分别用顺序查找、二分查找、二叉排序树
查找、平衡二叉树查找、哈希查找(用线性探查法 和拉链法)来实现查找,试画出它们的对应存储形 式(顺序查找的顺序表,二分查找的判定树,二叉 排序树查找的二叉排序树及平衡二叉树查找的平
① 构造好的哈希函数,使冲突尽可能的少 ② 设计有效的解决冲突的方法
1.2 哈希函数的构造方法
1.直接定址法
取关键字或关键字的某个线性函数值为散列地址,即 (K)=K 或 H(K)=a * K + b(其中a、b为常数)。
例:关键码集合为{ 100,300,500,700,800,900 }, 选取哈希函数为 Hash(key)=key/100,则存储结构(哈希 表)如下: 0 1 2 3 4 5 6 7 8 9
例 关键码集为 {47,7,29,11,16,92,22,8,3},
设:哈希表表长为m=11;
哈希函数为Hash(key)=key mod 11; 拟用线性探测法处理冲突。建哈希表:
0
1
2
3
4
5
6
3
7
7
8
29
9
8
10
11 22
47 92 16
线性探测法的优点:只要哈希表未被填满,保证 能找到一个空地址单元存放有冲突的元素; 线性探测法的缺点:可能使第i 个哈希地址的同 义词存入第i+1 个哈希地址,这样本应存入 第i+1个哈希地址的元素变成了第i+2个哈希 地址的同义词,……,
二分查找的判定树(中序序列为从小到大排列的 有序序列)
4
2
11
1
3
10
21
78
由图可得:二分查找的成功平均查找长度为
ASL=(1+2*2+3*4+4)/8=2.625
二叉排序树(关键字顺序已确定,该二叉排序树应唯一) 如图(a)所示,平衡二叉树(关键字顺序已确定,该平衡二 叉树也应该是唯一的),如图(b)所示。
因此,可能出现很多元素在相邻的哈希地址 上“堆积”起来,大大降低了查找效率。 可采用二次探测法或伪随机探测法,以改善 “堆积”问题。
1.开放地址法
(2)二次探测法 二次探测法对应的探查地址序列的计算公式为:
Hi = ( H(k)+di ) mod m
其中di =12,-12,22,-22,…,j2,-j2 (j≤m/2)。
(2)关键字的长度;
(3)哈希表的大小;
(4)关键字的分布情况; (5)记录的查找频率。
1.3 处理冲突的方法
1.开放地址法
开放地址就是表中尚未被占用的地址,当新插入的记
录所选地址已被占用时,即转而寻找其它尚开放的地址。
(1)线性探测法 设散列函数 H(K)=K mod m (m为表长),若发生 冲突,则沿着一个探查序列逐个探查,那么,第i次计算 冲突的散列地址为: Hi=(H(K)+di) mod m (di=1,2,…,m-1)
衡二叉树,两种哈希查找的哈希表 ),并求出每一
种查找的成功平均查找长度。设哈希函数为: H(k)=k mod 11,哈希表表长m=11。
顺序查找的顺序表(一维数组)
0 1 2 3 1 4 3 5 2 6 7 8 9 10 11 78 10 4 21
由图可得:顺序查找的成功平均查找长度为
ASL=(1+2+3+4+5+6+7+8)/8=4.5
序号 H(K) 1 1 2 2 5 3 4 9 11 5 6 7 8 21 9 10 27
13 16
2.冲突 两个不同的关键字具有相同的存储位置。
序号 H(K) 1 1 2 5 3 4 9 5 6 7 8 21 9 10 27
13 16
2
11
3.哈希表
根据设定的哈希函数 H(key) 和处理冲突的方
法将一组关键字映象到一个有限的连续的地址集
(区间)上,并以关键字在地址集中的“象”作
为记录在表中的存储位置,这种表便称为哈希表, 这一映象过程称为哈希造表或散列,所得存储位 置称为哈希地址或散列地址。
在哈希存储中,若发生冲突,则必须采取特殊的方法
来解决冲突问题,才能使哈希查找能顺利进行。虽然冲突 不可避免,但发生冲突的可能性却与三个方面因素有关。
11 22
(j≤m/2) 9 10
8
2
3
3
4
5
6
7
7
8
29
47 92 16
若di=伪随机序列,就称为伪随机探测法
2.链地址法
基本思想: 将具有相同哈希地址的记录链成一个单链表,
m个哈希地址就设 m个单链表,然后用一个数组
将m个单链表的表头指针存储起来,形成一个动
态的结构。
优点:插入、删除方便。
缺点:占用存储空间多。
(1)装填因子α; 装填因子是指哈希表中己存入的元素个数 n 与哈希 表的大小 m 的比值,即α=n/m(α<=1)。 α越小,发生冲突的可能性越小,反之,发生冲突的 可能性就越大。但是,α太小又会造成大量存贮空间的浪 费,因此必须兼顾存储空间和冲突两个方面。
(2)所构造的哈希函数;
(3)解决冲突的方法。
例:设有一组关键字{ 19, 01, 23, 14, 55, 20,
84, 27, 68, 11, 10, 77 } ,采用哈希函数为:
H(k)=k mod 13。采用开放地址的线性探测法解
决冲突,试在0~18的散列地址空间中,对该关键 字序列构造哈希表。 解:依题意 m=19,得到线性探测法对应的探查地
100 300 500 700 800 900
优点:以关键码 key 的某个线性函数值为哈希地址,不会 产生冲突。 缺点:要占用连续地址空间,空间效率低。
2.除后余数法
取关键字被不大于散列表表长 m 的数 p 除后所得的 余数为哈希函数。即 H(K)=K mod p (p≤m) 经验得知:一般可选 p 为质数或不包含小于 20 的质因 素的合数。
7
8 10
3.再哈希法 Hi= RHi(key) RHi 均是不同的哈希函数,即在同义词产生 地址冲突时计算另一个哈希函数地址,直到冲突
不再发生。不易产生“聚集”,但是增加了计算
时间。 4.建立一个公共溢出区
1.4 哈希表的查找及其分析
散列表的目的主要是用于快速查找。 在建表时采用何种散列函数及何种解决冲 突的办法,在查找时,也采用同样的散列函数 及解决冲突的办法。
^ ^ ^ ^ ^
10 21 ^
由图可得:链地址法的成功平均查找长度为
ASL=(1*6+2*2)/8=1.25
小结
1. 掌握查找的基本概念; 2. 熟练掌握静态查找表的查找算法思想并灵 活应用; 3. 熟练掌握动态查找表的特点以及二叉排序 树、平衡二叉树的各种操作思想 4. 了解B-、B+树的概念及插入和删除操作; 5. 熟练掌握哈希表的基本概念、哈希函数的 构造方法和解决冲突的方法,并能计算平均查找 长度。
址序列计算公式为:
di=(H(k)+j) mod 19; j=1,2,……,18
其计算函数如下:
{19,01,23,14,55,20,84,27,68,11,10,77}
H(19)=19 mod 13=6 H(01)=01 mod 13=1 H(23)=23 mod 13=10 H(84)=84 mod 13=6 (冲突) H(84)=(6+1) mod 19=7 (冲突)
3.平方取中法
取关键字平方后的中间几位为哈希函数。因为中间几 位与数据的每一位都相关。
例:2589的平方值为6702921,可以取中间的029为地址。
4.数字分析法
选用关键字的某几位组合成哈希地址。 选用原则应当是:各种符号在该位上出现的频率大致相同。 例:有一组(例如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 可作为哈希地址选用。 3 4 9 8 0 5 8 ② 若哈希地址取两位(因元素仅80 3 4 7 9 6 7 1 个),则可取这四位中的任意两位组 3 4 7 3 9 1 9 合成哈希地址,也可以取其中两位与 位号:① ② ③ ④ ⑤ ⑥ ⑦ 其它两位叠加求和后,取低两位作哈 希地址。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
相关文档
最新文档