哈希表
- 格式:docx
- 大小:32.20 KB
- 文档页数:5
哈希表的用法
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。
也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。
这个映射函数叫做散列函数,存放记录的数组叫做散列表。
哈希表的主要用法包括:
1.插入元素:向哈希表中添加新的元素。
这通常涉及到使用哈希函数来计算元素的关键码值对应的存储位置,并将元素存储在该位置。
2.查找元素:在哈希表中查找特定的元素。
这同样需要使用哈希函数来计算元素的关键码值对应的存储位置,然后检查该位置是否有相应的元素。
3.删除元素:从哈希表中移除指定的元素。
这涉及到找到元素的存储位置,并将其从表中删除。
哈希表的时间复杂度通常是O(1),这意味着无论哈希表中有多少元素,插入、查找和删除操作都可以在常数时间内完成。
然而,这取决于哈希函数的选择和冲突解决策略。
如果哈希函数设
计得不好或者冲突解决策略不合适,可能会导致性能下降。
此外,哈希表还有一些其他的应用,例如用于实现关联数组、缓存系统、去重处理等等。
hash分布表
(原创实用版)
目录
1.哈希表的概念与特点
2.哈希表的实现方法
3.哈希表的应用场景
4.哈希表的优缺点
正文
哈希表是一种基于数组实现的数据结构,它通过哈希函数将键映射到数组的一个位置,从而实现快速插入和查询。
哈希表的特点是插入、查询、删除操作的时间复杂度都是 O(1),因此它在很多场景下具有较高的应用价值。
哈希表的实现方法主要依赖于哈希函数的设计。
哈希函数可以将任意大小的数据映射为一个固定大小的数据,通常是某个质数的幂次方。
常见的哈希函数设计方法包括:直接定址法、除留余数法、数字分析法、平方取中法、折叠法等。
在实际应用中,根据数据特点和性能要求,可以选择合适的哈希函数。
哈希表的应用场景非常广泛,包括但不限于以下几种:
1.数据查找:哈希表可以实现快速查找,适合处理大量数据的场景。
2.数据插入:哈希表插入操作的时间复杂度为 O(1),适合处理频繁插入数据的场景。
3.数据删除:哈希表删除操作的时间复杂度为 O(1),适合处理频繁删除数据的场景。
尽管哈希表具有很多优点,但它仍然存在一些缺点:
1.哈希表的存储空间利用率较低,因为数组需要预留一定的空间以应对哈希冲突。
2.哈希冲突会影响性能,当哈希表中的数据量较大时,冲突的概率也会增加,导致查询速度下降。
总之,哈希表是一种高效、实用的数据结构,它可以在很多场景下提高数据处理效率。
c语言中哈希表用法在C语言中,哈希表(hash table)是一种数据结构,用于存储键值对(key-value pairs)。
它利用哈希函数(hash function)将键映射到一个特定的索引,然后将该索引用于在数组中存储或查找相应的值。
使用哈希表可以实现高效的数据查找和插入操作。
下面是哈希表的基本用法:1.定义哈希表的结构体:```ctypedef struct {int key;int value;} hash_table_entry;typedef struct {int size;hash_table_entry **buckets;} hash_table;```2.初始化哈希表:```chash_table *create_hash_table(int size) {hash_table *ht = (hash_table*)malloc(sizeof(hash_table));ht->size = size;ht->buckets = (hash_table_entry**)calloc(size,sizeof(hash_table_entry*));return ht;}```在初始化时,需要定义哈希表的大小(桶的数量)。
3.计算哈希值:```cint hash_function(int key, int size) {//根据具体需求实现哈希函数,例如对key取余操作return key % size;}```哈希函数将key映射到哈希表的索引位置。
4.插入键值对:```cvoid insert(hash_table *ht, int key, int value) { //计算哈希值int index = hash_function(key, ht->size);//创建新的哈希表节点hash_table_entry *entry =(hash_table_entry*)malloc(sizeof(hash_table_entry));entry->key = key;entry->value = value;//将节点插入到相应的桶中if (ht->buckets[index] == NULL) {ht->buckets[index] = entry;} else {//处理哈希冲突,例如使用链表或开放定址法解决冲突//这里使用链表来处理冲突hash_table_entry *current = ht->buckets[index];while (current->next != NULL) {current = current->next;current->next = entry;}}```5.查找值:```cint get(hash_table *ht, int key) {int index = hash_function(key, ht->size);hash_table_entry *current = ht->buckets[index]; //在相应的桶中查找相应的值while (current != NULL) {if (current->key == key) {return current->value;current = current->next;}return -1; //未找到对应的值}```哈希表的优点是它具有快速的查找和插入操作,平均情况下的查找和插入时间复杂度为O(1)。
哈希表(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.不仅可以对关键字直接取模,也可在折叠、平⽅取中等运算之后取模。
哈希表的定义和工作原理哈希表,又称散列表,是一种数据结构,用于实现键值对的存储和快速查找。
它基于哈希函数将键映射到表中的位置,从而实现快速的插入、删除和查找操作。
定义哈希表是一种包含键值对的数据结构,其中每个键都是唯一的。
它通过哈希函数将键映射到表中的位置,以便快速访问值。
在哈希表中,每个位置称为“桶”,存储一个或多个键值对。
工作原理1. 哈希函数哈希表的核心是哈希函数。
这个哈希函数接受一个键作为输入,并输出一个与该键对应的位置。
理想的哈希函数应该能将键均匀地映射到表中的不同位置,以避免冲突。
2. 处理冲突由于哈希函数的有限性,可能会出现不同的键映射到相同的位置,造成冲突。
为解决冲突,哈希表使用一些技术,如链地址法和开放寻址法。
在链地址法中,每个桶存储一个链表或者树来存储冲突的键值对;而在开放寻址法中,桶冲突时会尝试在其他位置继续寻找空闲位置。
3. 插入操作对于插入操作,首先通过哈希函数计算键的位置。
如果该位置为空,则直接插入键值对;如果位置已被占用,则根据冲突处理策略选择合适的位置进行插入。
4. 查找操作查找操作也是通过哈希函数计算键的位置。
如果该位置为空,则表示键不存在;如果位置不为空,则通过搜索冲突处理的技术在桶中查找对应的值。
5. 删除操作删除操作首先需要查找键在哈希表中的位置。
如果该位置为空,则键不存在;如果位置不为空,则根据冲突处理策略删除对应的键值对。
总结哈希表是一种高效的数据结构,能够实现快速的插入、查找和删除操作。
通过合适的哈希函数和冲突处理策略,可以使哈希表具有良好的性能。
对于大规模数据集合的存储和检索,哈希表是一种十分实用的工具。
hashmap数据结构特点
哈希表(HashMap)是一种常见的数据结构,具有以下特点:
1. 快速的查找和插入,哈希表通过将键映射为索引来实现快速
的查找和插入操作。
对于给定的键,哈希函数可以将其转换为索引,从而快速定位对应的值。
2. 灵活的键值对存储,哈希表以键值对的形式存储数据,这使
得它非常灵活,可以存储各种类型的数据,并且可以根据键快速地
访问对应的值。
3. 高效的内存利用,哈希表在内存中以数组的形式存储,通过
哈希函数将键映射为数组的索引。
这种方式可以高效地利用内存空间。
4. 碰撞处理,由于哈希函数的映射不是一一对应的,可能会出
现不同键映射到相同索引的情况,即碰撞。
哈希表需要解决碰撞问题,常见的解决方法包括链地址法和开放寻址法。
5. 迭代性能较差,哈希表在迭代时性能较差,因为它是基于哈
希函数的存储结构,无法像数组那样顺序地访问元素。
在迭代时需
要遍历整个哈希表来获取所有的键值对。
总的来说,哈希表是一种高效的数据结构,适合用于需要快速
查找和插入的场景,但在迭代方面稍显不足。
对于大部分应用场景,哈希表都是一种非常实用的数据结构。
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表,来探讨其实现原理、方法以及与其他数据结构的对比。
哈希表常数一、什么是哈希表哈希表(Hash Table),也称为散列表,是一种用于存储键值对的数据结构。
它通过将关键字映射到表中的一个位置来实现快速的检索。
哈希表的核心思想是利用哈希函数将关键字转化为一个整数,然后将该整数作为数组的索引,将值存储在该位置上。
这样,当需要查找或插入一个元素时,只需要通过哈希函数计算出对应的索引,就可以直接访问到该元素,从而实现了O(1)的平均时间复杂度。
二、哈希函数的作用哈希函数是哈希表的核心组成部分,它负责将关键字转化为一个整数。
一个好的哈希函数应该具备以下特点:1.均匀性:哈希函数应该将关键字均匀地映射到整数空间中,以确保不同的关键字能够均匀地分布在哈希表中,避免出现过多的冲突。
2.简单性:哈希函数应该具有高效的计算性能,尽量避免复杂的计算操作,以提高哈希表的插入和查找效率。
3.低冲突性:哈希函数应该尽量减少冲突的发生,即不同的关键字映射到同一个位置的概率应该尽量小。
三、常用的哈希函数1. 直接地址法直接地址法是一种最简单的哈希函数,它直接将关键字作为数组的索引。
这种方法适用于关键字的范围比较小的情况,但当关键字范围很大时,会导致数组过大,造成空间的浪费。
2. 除留余数法除留余数法是一种常用的哈希函数,它将关键字除以一个不大于哈希表长度的数,然后取余数作为数组的索引。
这种方法可以保证关键字均匀地分布在哈希表中,但当哈希表长度和关键字的公因数不为1时,会导致冲突的发生。
3. 平方取中法平方取中法是一种通过对关键字平方后再取中间几位数作为索引的哈希函数。
这种方法可以在一定程度上减少冲突的发生,但对于关键字的选择有一定的要求。
4. 折叠法折叠法是一种将关键字分割成若干部分,然后将这些部分相加作为索引的哈希函数。
这种方法可以在一定程度上增加哈希函数的均匀性,但需要注意分割的方式和相加的顺序。
四、哈希表的常数哈希表的常数是指在哈希表操作中的一些常数因子,它们对哈希表的性能有一定的影响。
1. 哈希表的概念
对于动态查找表而言,1) 表长不确定;2)在设计查找表时,只知道关键字所属范围,而不
知道确切的关键字。
因此,一般情况需建立一个函数关系,以f(key)作为关键字为key的
录在表中的位置,通常称这个函数f(key)为哈希函数。
(注意:这个函数并不一定是数学函
数)
例如:对于如下9个关键字
从这个例子可以看出:
1. 哈希函数是一个映象,即:将关键字的集合映射到某个地址集合上,它的设置很灵活,
只要这个地址集合的大小不超出允许范围即可;
2. 由于哈希函数是一个压缩映象,因此,在一般情况下,很容易产生“冲突”现象,即:
key1 key2,而 f(key1) = f(key2) 并且,改进哈希函数只能减少冲突,而不能避免冲突。
因此,在设计哈希函数时,一方面要考虑选择一个“好”的哈希函数;另一方面要选择一种
处理冲突的方法。
所谓“好”的哈希函数,指的是对于集合中的任意一个关键字,经哈希函
数“映象”到地址集合中任何一个地址的概率是相同的,称这类哈希函数为“均匀的”哈希
函数。
根据设定的哈希函数H(key)和所选中的处理冲突的方法,将一组关键字映象到一个有限的地
址连续的地址集(区间)上,并以关键字在地址集中的“象”作为相应记录在表中的存储位置,
这种表被称为哈希表。
哈希表是基于哈希函数建立的一种查找表。
2. 哈希函数的构造方法
对数字的关键字可有下列哈希函数的构造方法,若是非数字关键字,则需先对其进行数字化
处理。
(1)直接定址法
哈希函数为关键字的线性函数:
H(key) = key 或者 H(key) = a key + b
适用场合:地址集合的大小 = 关键字集合的大小
(2)数字分析法
假设关键字集合中的每个关键字都是由s位数字组成(k1, k2, …, kn),分析关键字集中的
全体,并从中提取分布均匀的若干位或它们的组合作为地址。
适用场合:能预先估计出全体关键字的每一位上各种数字出现的频度。
(3)平方取中法
若关键字的每一位都有某些数字重复出现频度很高的现象,则先求关键字的平方值,以通过
“平方”扩大差别,同时平方值的中间几位受到整个关键字中各位的影响; (4)折叠法
若关键字的位数特别多,则可将其分割成几部分,然后取它们的叠加和为哈希地址。
可有:
移位叠加和间界叠加两种处理方法。
(5)除留余数法
H(key) = key MOD p p≤m (表长)
关键问题是:如何选取 p ?
p 应为不大于m 的质数或是不含20以下的质因子
例如:key = 12, 39, 18, 24, 33, 21 时,若取 p=9, 则使所有含质因子3的关键字均映射
到地址0, 3, 6 上,从而增加了“冲突”的可能性。
(6)随机数法
H(key) = Random(key)实际造表时,采用何种构造哈希函数的方法取决于建表的关键字集合
的情况(包括关键字的范围和形态),总的原则是使产生冲突的可能性降到尽可能地小。
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
增量 di 有三种取法:
1)性探测再散列
di = c i 最简单的情况 c=1
2)方探测再散列
di = 12, -12, 22, -22, …,
3)机探测再散列
di 是一组伪随机数列
注意:增量di应具有“完备性”,即产生的Hi均不相同,且所产生的s(m-1)个Hi值能覆盖
哈希表中所有的地址。
要求:
· 平方探测时的表长m必为4j+3的质数;
· 随机探测时的m和di没有公因子。
(2)链地址法
将所有哈希地址相同的记录都链接在同一链表中。
线性探测容易产生二次聚集,链地址肯定不会产生二次聚集。
一次聚集的产生主要取决于哈希函数,在哈希函数均匀的前提下,可以认为没有一次聚集。
4. 哈希表的查找
查找过程和造表过程一致。
假设采用开放定址处理冲突,则查找过程为: 对于给定值K, 计算哈希地址 i = H(K) ,若r[i] = NULL 则查找不成功;
若 r[i].key = K 则查找成功, 否则求下一地址Hi,直至r[Hi] = NULL (查找不成功)
或r[Hi].key = K (查找成功)为止。
//--- 开放定址哈希表的存储结构 ---
int hashsize[] = { 997, ... }; // 哈希表容量递增表,一个合适的素数序列typedef struct {
ElemType *elem; // 数据元素存储基址,动态分配数组
int count; // 当前数据元素个数
int sizeindex; // hashsize[sizeindex]为当前容量
} HashTable;
#define SUCCESS 1
#define UNSUCCESS 0
#define DUPLICATE -1
Status SearchHash (HashTable H, KeyType K, int &p,
int &c) {
// 在开放定址哈希表H中查找关键码为K的元素,若查找成功,以p指示待查
数据元素在表中//位置,并返回SUCCESS;否则,以p指示插入位置,并返回UNSUCCESS, c用以计冲突次数,//其初值置零,供建表插入时参考
p = Hash(K); // 求得哈希地址
while ( H.elem[p].key != NULLKEY && !EQ(K, H.elem[p].key))
// 该位置中填有记录并且关键字不相等
collision(p, ++c); // 求得下一探查地址p
if (EQ(K, H.elem[p].key)) return SUCCESS; // 查找成功,p返回待查数据元素位置
else return UNSUCCESS;// 查找不成功,p返回的是插入位置
} // SearchHash
通过调用查找算法实现了开放定址哈希表的插入操作。
Status InsertHash (HashTable &H, Elemtype e) {
// 查找不成功时插入数据元素e到开放定址哈希表H中,并返回OK;若冲突次数过大,则//重建哈希表
c = 0;
if ( HashSearch ( H, e.key, p, c ) == SUCCESS )
return DUPLICATE; // 表中已有与e有相同关键字的元素
else if ( c < hashsize[H.sizeindex]/2 ) { // 冲突次数c未达到上限,(阀值c可调)
H.elem[p] = e; ++H.count; return OK; // 插入e
}
else RecreateHashTable(H); // 重建哈希表
} // InsertHash
可见,无论查找成功与否,ASL均不为零。
决定哈希表查找的ASL的因素:
1. 选用的哈希函数;
2. 选用的处理冲突的方法;
3. 哈希表饱和的程度,装载因子α=n/m 值的大小一般情况下,可以认为选用的哈希函数是
“均匀”的,则在讨论ASL时,可以不考虑它的因素。
哈希表的ASL是处理冲突方法和装载因子的函数。
可以证明查找成功时有下列结果:
5. 哈希表的删除操作
从哈希表中删除记录时,要作特殊处理,相应地要修改查找算法对静态查找表,有时
也可能找到不发生冲突的哈希函数。
即此时的哈希表的ASL=0, 此类哈希函数为理想
(perfect)的哈希函数。