哈希表
哈希表统计次数

哈希表统计次数哈希表是一种常用的数据结构,用于统计元素的出现次数。
它通过将元素映射到一个固定的位置来实现快速的插入和查找操作。
在本文中,我们将探讨哈希表统计次数的原理、应用场景以及一些相关的算法。
一、哈希表统计次数的原理哈希表是由一个数组和一个哈希函数组成的。
哈希函数将元素映射到数组的某个位置上,并将元素存储在该位置上。
当需要统计元素的次数时,只需要通过哈希函数找到元素所在的位置,并将该位置上的计数器加一即可。
哈希表统计次数广泛应用于各种领域,例如文本处理、数据分析、网络流量分析等。
在文本处理中,可以使用哈希表统计单词的出现次数,从而得到文本的词频分布。
在数据分析中,可以使用哈希表统计用户的行为次数,从而了解用户的使用习惯。
在网络流量分析中,可以使用哈希表统计IP地址的访问次数,从而发现异常访问行为。
三、哈希表统计次数的算法1. 链地址法链地址法是最常见的解决哈希冲突的方法之一。
它将哈希表的每个位置都设置为一个链表,当发生哈希冲突时,将冲突的元素插入到链表的末尾。
这样,可以保证每个位置上的元素都能被找到,并且插入和查找的时间复杂度为O(1)。
2. 开放地址法开放地址法是另一种解决哈希冲突的方法。
它通过线性探测、二次探测、双重哈希等方式来寻找下一个可用的位置。
当发生哈希冲突时,会依次探测下一个位置,直到找到一个空闲的位置或者遍历完整个哈希表。
这样,可以保证每个元素都能被插入到哈希表中,并且插入和查找的时间复杂度为O(1)。
四、哈希表统计次数的优化1. 负载因子负载因子是指哈希表中已经存储的元素个数与哈希表长度的比值。
当负载因子过大时,哈希冲突的概率会增大,从而影响插入和查找的性能。
因此,可以通过调整负载因子来优化哈希表的性能。
2. 哈希函数哈希函数的选择对哈希表的性能有着重要影响。
一个好的哈希函数应该能够将元素均匀地映射到不同的位置上,从而减少哈希冲突的概率。
常见的哈希函数有除法取余法、乘法取整法、平方取中法等。
哈希硅表工作原理

哈希硅表工作原理哈希表是一种通过将键值对映射到数组索引位置来实现高效查找的数据结构。
它的工作原理可以分为以下几个步骤:1. 数组初始化:哈希表首先会根据预设的大小,创建一个固定长度的数组,并初始化为默认值(如NULL)。
2. 哈希函数:当需要插入或查找一个键值对时,会利用哈希函数将键转化为一个对应的哈希值。
哈希函数的设计要尽可能地将不同的键映射到不同的索引位置上,避免冲突。
3. 冲突解决:由于不同的键可能会得到相同的哈希值,所以可能会发生冲突。
冲突解决的方法通常有两种:链地址法和开放地址法。
- 链地址法:每个哈希表的索引位置上存储的是一个链表,冲突的项会连接在同一个索引位置的链表上。
- 开放地址法:当冲突发生时,会通过一定的规则(如线性探测、二次探测等)找到下一个可用的位置进行存储。
4. 插入操作:通过哈希函数计算键的哈希值,并根据哈希值找到对应的索引位置。
如果这个位置上已经有其他键值对存在,则根据冲突解决方法找到下一个可用的位置。
将键值对存储到这个位置上。
5. 查找操作:通过哈希函数计算键的哈希值,并根据哈希值找到对应的索引位置。
如果这个位置上存储的值与要查找的键相等,则返回对应的值。
如果这个位置上存储的值不等于要查找的键,并且存在链表,则在链表中继续查找。
6. 删除操作:通过哈希函数计算键的哈希值,并根据哈希值找到对应的索引位置。
如果这个位置上存储的值与要删除的键相等,则将这个位置置为空。
如果这个位置上存储的值不等于要删除的键,并且存在链表,则在链表中继续查找并删除。
通过以上的步骤,哈希表可以实现高效的查找操作,时间复杂度通常为O(1)。
但是在存在大量冲突的情况下,查找的效率会下降,这时需要选择合适的哈希函数或调整哈希表的大小来避免冲突。
哈希表的定义查找及分析bklt

一、直接地址法
:
取关键字或关键字的某个线性函值为哈希地址
即: H(key) = key 或: H(key) = a* key + b
其中,a, b为常数。
二、数字分析法
假设关键字集合中的每个关键字都是由 s 位 数字组成 (u1, u2, …, us),分析关键字集中的全 体, 并从中提取分布均匀的若干位或它们的组 合作为地址。
查找不成功时的ASL
ASLunsucc=( )/11
= /11
10
11
3
8
线性探测再散列的优点:
只要哈希表未满,总能找到一个空地址。
缺点:会产生二次聚集。
01…
70 19 33 18
5678 9
… 12
9
二、 链地址法
在哈希表的每一个单元中存放一个指针,将所 有的同义词连成一个链表。 假设哈希地址在区间[0 .. m-1]上,则哈希表为
一个指针数组。
typedef struct node{ //定义链表中结点 KeyType key; 其它成员 ; struct node *next;
} Node,*Nodeptr; typedef Nodeptr chainhash[m];// 定义指针数组10
例1: 关键字集合 { 19, 01, 23, 14, 55, 68, 11, 82, 36 }
若采用线性探测再散列处理冲突
0 1 2 3 4 5 6 7 8 9 10
55 01 23 14 68 11 82 36 19
11 21 3 62 5 1
若采用二次探测再散列处理冲突
0 1 2 3 4 5 6 7 8 9 10
哈希表——线性探测法、链地址法、查找成功、查找不成功的平均长度

哈希表——线性探测法、链地址法、查找成功、查找不成功的平均长度⼀、哈希表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. 单词计数器:在文本处理中,我们经常需要统计某个单词在文本中出现的次数。
我们可以使用哈希表来实现一个简单的单词计数器,将单词作为键,出现的次数作为值。
这样,当需要统计某个单词的出现次数时,可以直接通过键进行查找。
6. 数组去重:在一组数据中,我们经常需要去除重复的元素。
我们可以使用哈希表来实现数组的去重功能,将数组中的元素作为键,出现的次数作为值。
这样,当需要判断某个元素是否重复时,可以直接通过键进行查找。
7. URL短链接:在互联网中,我们经常需要将较长的URL转换为较短的URL,以方便用户分享和记忆。
我们可以使用哈希表来实现URL 的短链接功能,将长URL作为键,短URL作为值。
这样,当用户访问短URL时,可以通过哈希表快速地定位到对应的长URL。
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表,来探讨其实现原理、方法以及与其他数据结构的对比。
哈希表常数

哈希表常数一、什么是哈希表哈希表(Hash Table),也称为散列表,是一种用于存储键值对的数据结构。
它通过将关键字映射到表中的一个位置来实现快速的检索。
哈希表的核心思想是利用哈希函数将关键字转化为一个整数,然后将该整数作为数组的索引,将值存储在该位置上。
这样,当需要查找或插入一个元素时,只需要通过哈希函数计算出对应的索引,就可以直接访问到该元素,从而实现了O(1)的平均时间复杂度。
二、哈希函数的作用哈希函数是哈希表的核心组成部分,它负责将关键字转化为一个整数。
一个好的哈希函数应该具备以下特点:1.均匀性:哈希函数应该将关键字均匀地映射到整数空间中,以确保不同的关键字能够均匀地分布在哈希表中,避免出现过多的冲突。
2.简单性:哈希函数应该具有高效的计算性能,尽量避免复杂的计算操作,以提高哈希表的插入和查找效率。
3.低冲突性:哈希函数应该尽量减少冲突的发生,即不同的关键字映射到同一个位置的概率应该尽量小。
三、常用的哈希函数1. 直接地址法直接地址法是一种最简单的哈希函数,它直接将关键字作为数组的索引。
这种方法适用于关键字的范围比较小的情况,但当关键字范围很大时,会导致数组过大,造成空间的浪费。
2. 除留余数法除留余数法是一种常用的哈希函数,它将关键字除以一个不大于哈希表长度的数,然后取余数作为数组的索引。
这种方法可以保证关键字均匀地分布在哈希表中,但当哈希表长度和关键字的公因数不为1时,会导致冲突的发生。
3. 平方取中法平方取中法是一种通过对关键字平方后再取中间几位数作为索引的哈希函数。
这种方法可以在一定程度上减少冲突的发生,但对于关键字的选择有一定的要求。
4. 折叠法折叠法是一种将关键字分割成若干部分,然后将这些部分相加作为索引的哈希函数。
这种方法可以在一定程度上增加哈希函数的均匀性,但需要注意分割的方式和相加的顺序。
四、哈希表的常数哈希表的常数是指在哈希表操作中的一些常数因子,它们对哈希表的性能有一定的影响。
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、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
哈希表(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)于是本篇米有了哦~。