哈希表
哈希表统计次数
![哈希表统计次数](https://img.taocdn.com/s3/m/44d34cb98662caaedd3383c4bb4cf7ec4afeb6ca.png)
哈希表统计次数哈希表是一种常用的数据结构,用于统计元素的出现次数。
它通过将元素映射到一个固定的位置来实现快速的插入和查找操作。
在本文中,我们将探讨哈希表统计次数的原理、应用场景以及一些相关的算法。
一、哈希表统计次数的原理哈希表是由一个数组和一个哈希函数组成的。
哈希函数将元素映射到数组的某个位置上,并将元素存储在该位置上。
当需要统计元素的次数时,只需要通过哈希函数找到元素所在的位置,并将该位置上的计数器加一即可。
哈希表统计次数广泛应用于各种领域,例如文本处理、数据分析、网络流量分析等。
在文本处理中,可以使用哈希表统计单词的出现次数,从而得到文本的词频分布。
在数据分析中,可以使用哈希表统计用户的行为次数,从而了解用户的使用习惯。
在网络流量分析中,可以使用哈希表统计IP地址的访问次数,从而发现异常访问行为。
三、哈希表统计次数的算法1. 链地址法链地址法是最常见的解决哈希冲突的方法之一。
它将哈希表的每个位置都设置为一个链表,当发生哈希冲突时,将冲突的元素插入到链表的末尾。
这样,可以保证每个位置上的元素都能被找到,并且插入和查找的时间复杂度为O(1)。
2. 开放地址法开放地址法是另一种解决哈希冲突的方法。
它通过线性探测、二次探测、双重哈希等方式来寻找下一个可用的位置。
当发生哈希冲突时,会依次探测下一个位置,直到找到一个空闲的位置或者遍历完整个哈希表。
这样,可以保证每个元素都能被插入到哈希表中,并且插入和查找的时间复杂度为O(1)。
四、哈希表统计次数的优化1. 负载因子负载因子是指哈希表中已经存储的元素个数与哈希表长度的比值。
当负载因子过大时,哈希冲突的概率会增大,从而影响插入和查找的性能。
因此,可以通过调整负载因子来优化哈希表的性能。
2. 哈希函数哈希函数的选择对哈希表的性能有着重要影响。
一个好的哈希函数应该能够将元素均匀地映射到不同的位置上,从而减少哈希冲突的概率。
常见的哈希函数有除法取余法、乘法取整法、平方取中法等。
哈希硅表工作原理
![哈希硅表工作原理](https://img.taocdn.com/s3/m/50507f327ed5360cba1aa8114431b90d6d858964.png)
哈希硅表工作原理哈希表是一种通过将键值对映射到数组索引位置来实现高效查找的数据结构。
它的工作原理可以分为以下几个步骤:1. 数组初始化:哈希表首先会根据预设的大小,创建一个固定长度的数组,并初始化为默认值(如NULL)。
2. 哈希函数:当需要插入或查找一个键值对时,会利用哈希函数将键转化为一个对应的哈希值。
哈希函数的设计要尽可能地将不同的键映射到不同的索引位置上,避免冲突。
3. 冲突解决:由于不同的键可能会得到相同的哈希值,所以可能会发生冲突。
冲突解决的方法通常有两种:链地址法和开放地址法。
- 链地址法:每个哈希表的索引位置上存储的是一个链表,冲突的项会连接在同一个索引位置的链表上。
- 开放地址法:当冲突发生时,会通过一定的规则(如线性探测、二次探测等)找到下一个可用的位置进行存储。
4. 插入操作:通过哈希函数计算键的哈希值,并根据哈希值找到对应的索引位置。
如果这个位置上已经有其他键值对存在,则根据冲突解决方法找到下一个可用的位置。
将键值对存储到这个位置上。
5. 查找操作:通过哈希函数计算键的哈希值,并根据哈希值找到对应的索引位置。
如果这个位置上存储的值与要查找的键相等,则返回对应的值。
如果这个位置上存储的值不等于要查找的键,并且存在链表,则在链表中继续查找。
6. 删除操作:通过哈希函数计算键的哈希值,并根据哈希值找到对应的索引位置。
如果这个位置上存储的值与要删除的键相等,则将这个位置置为空。
如果这个位置上存储的值不等于要删除的键,并且存在链表,则在链表中继续查找并删除。
通过以上的步骤,哈希表可以实现高效的查找操作,时间复杂度通常为O(1)。
但是在存在大量冲突的情况下,查找的效率会下降,这时需要选择合适的哈希函数或调整哈希表的大小来避免冲突。
哈希表的定义查找及分析bklt
![哈希表的定义查找及分析bklt](https://img.taocdn.com/s3/m/12619eb318e8b8f67c1cfad6195f312b3069eb6e.png)
一、直接地址法
:
取关键字或关键字的某个线性函值为哈希地址
即: 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
哈希表——线性探测法、链地址法、查找成功、查找不成功的平均长度
![哈希表——线性探测法、链地址法、查找成功、查找不成功的平均长度](https://img.taocdn.com/s3/m/32add13d30126edb6f1aff00bed5b9f3f90f72e5.png)
哈希表——线性探测法、链地址法、查找成功、查找不成功的平均长度⼀、哈希表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.不仅可以对关键字直接取模,也可在折叠、平⽅取中等运算之后取模。
哈希表简单例子
![哈希表简单例子](https://img.taocdn.com/s3/m/3bb82cd4988fcc22bcd126fff705cc1755275f89.png)
哈希表简单例子哈希表是一种常用的数据结构,它可以用来存储键值对,并且能够以常数时间复杂度进行查找、插入和删除操作。
在这篇文章中,我将给大家列举一些哈希表的简单例子,希望能够帮助大家更好地理解和应用哈希表。
1. 学生信息管理系统:我们可以将学生的学号作为键,学生的姓名作为值,通过哈希表来管理学生的信息。
这样,我们就可以通过学生的学号快速地查找到对应的姓名,而不需要遍历整个数据集。
2. 图书馆借阅系统:我们可以将图书的编号作为键,借阅者的信息(如姓名、借书日期等)作为值,通过哈希表来管理图书的借阅情况。
这样,当有人借阅或归还图书时,我们可以快速地定位到对应的图书并更新借阅信息。
3. 联系人电话簿:我们可以将联系人的姓名作为键,电话号码作为值,通过哈希表来管理联系人的电话号码。
这样,当我们需要查找某个联系人的电话时,可以直接通过姓名进行查找,而不需要遍历整个电话簿。
4. 缓存系统:在计算机系统中,缓存用于存储经常访问的数据,以提高访问速度。
我们可以使用哈希表来实现缓存系统,将数据的关键字作为键,数据本身作为值。
这样,当需要访问某个数据时,可以首先在哈希表中查找,如果存在则直接返回,否则再从存储介质中读取数据。
5. 单词计数器:在文本处理中,我们经常需要统计某个单词在文本中出现的次数。
我们可以使用哈希表来实现一个简单的单词计数器,将单词作为键,出现的次数作为值。
这样,当需要统计某个单词的出现次数时,可以直接通过键进行查找。
6. 数组去重:在一组数据中,我们经常需要去除重复的元素。
我们可以使用哈希表来实现数组的去重功能,将数组中的元素作为键,出现的次数作为值。
这样,当需要判断某个元素是否重复时,可以直接通过键进行查找。
7. URL短链接:在互联网中,我们经常需要将较长的URL转换为较短的URL,以方便用户分享和记忆。
我们可以使用哈希表来实现URL 的短链接功能,将长URL作为键,短URL作为值。
这样,当用户访问短URL时,可以通过哈希表快速地定位到对应的长URL。
c实现的hash表-概述说明以及解释
![c实现的hash表-概述说明以及解释](https://img.taocdn.com/s3/m/5d1532b5f605cc1755270722192e453611665b5f.png)
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表,来探讨其实现原理、方法以及与其他数据结构的对比。
哈希表常数
![哈希表常数](https://img.taocdn.com/s3/m/f7105e64bf23482fb4daa58da0116c175e0e1e57.png)
哈希表常数一、什么是哈希表哈希表(Hash Table),也称为散列表,是一种用于存储键值对的数据结构。
它通过将关键字映射到表中的一个位置来实现快速的检索。
哈希表的核心思想是利用哈希函数将关键字转化为一个整数,然后将该整数作为数组的索引,将值存储在该位置上。
这样,当需要查找或插入一个元素时,只需要通过哈希函数计算出对应的索引,就可以直接访问到该元素,从而实现了O(1)的平均时间复杂度。
二、哈希函数的作用哈希函数是哈希表的核心组成部分,它负责将关键字转化为一个整数。
一个好的哈希函数应该具备以下特点:1.均匀性:哈希函数应该将关键字均匀地映射到整数空间中,以确保不同的关键字能够均匀地分布在哈希表中,避免出现过多的冲突。
2.简单性:哈希函数应该具有高效的计算性能,尽量避免复杂的计算操作,以提高哈希表的插入和查找效率。
3.低冲突性:哈希函数应该尽量减少冲突的发生,即不同的关键字映射到同一个位置的概率应该尽量小。
三、常用的哈希函数1. 直接地址法直接地址法是一种最简单的哈希函数,它直接将关键字作为数组的索引。
这种方法适用于关键字的范围比较小的情况,但当关键字范围很大时,会导致数组过大,造成空间的浪费。
2. 除留余数法除留余数法是一种常用的哈希函数,它将关键字除以一个不大于哈希表长度的数,然后取余数作为数组的索引。
这种方法可以保证关键字均匀地分布在哈希表中,但当哈希表长度和关键字的公因数不为1时,会导致冲突的发生。
3. 平方取中法平方取中法是一种通过对关键字平方后再取中间几位数作为索引的哈希函数。
这种方法可以在一定程度上减少冲突的发生,但对于关键字的选择有一定的要求。
4. 折叠法折叠法是一种将关键字分割成若干部分,然后将这些部分相加作为索引的哈希函数。
这种方法可以在一定程度上增加哈希函数的均匀性,但需要注意分割的方式和相加的顺序。
四、哈希表的常数哈希表的常数是指在哈希表操作中的一些常数因子,它们对哈希表的性能有一定的影响。
c++的hash表使用方法
![c++的hash表使用方法](https://img.taocdn.com/s3/m/9ea3ecc1fbb069dc5022aaea998fcc22bdd14359.png)
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() 成员函数查找指定元素是否存在。
数据结构.第9章.查找.4.哈希表
![数据结构.第9章.查找.4.哈希表](https://img.taocdn.com/s3/m/95dd488b71fe910ef12df8ba.png)
§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
9.3 哈希表
![9.3 哈希表](https://img.taocdn.com/s3/m/960b78db360cba1aa811da8d.png)
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为常数) 特点:直接定址法所得地址集合与关键字集合大小
哈希表是有序还是无序的 哈希表底层的数据结构实现 哈希表的构造算法 哈希表解决冲突的方法
![哈希表是有序还是无序的 哈希表底层的数据结构实现 哈希表的构造算法 哈希表解决冲突的方法](https://img.taocdn.com/s3/m/abd5bdc082d049649b6648d7c1c708a1294a0a58.png)
哈希表是有序还是无序的哈希表底层的数据结构实现哈希表的构造算法哈希表解决冲突的方法1. 引言1.1 概述哈希表是一种使用哈希函数和数组来实现的数据结构,具有高效的查找和插入操作的优点。
它通过将关键字映射到数组中的位置来实现快速查找。
在计算机科学领域中,哈希表被广泛应用于各种场景,如数据库索引、缓存、字典等。
本文将对哈希表的一些重要问题进行讨论和探究,包括哈希表是有序还是无序的问题、哈希表底层的数据结构实现、哈希表的构造算法以及解决冲突的方法。
通过深入研究这些问题,我们可以更好地理解和应用哈希表。
1.2 文章结构本文共分为六个部分,每个部分都涵盖了特定主题:第一部分为引言部分,介绍了文章的背景、目的以及整体结构。
第二部分将探讨哈希表是有序还是无序的问题。
我们首先对哈希表的定义和功能进行概述,然后讨论了哈希表顺序性问题可能存在的原因,并综合相关研究和理论观点进行综述。
第三部分将集中讨论哈希表底层的数据结构实现。
我们将介绍使用数组和链表来实现哈希表底层数据结构的方法,并讨论其他可能用于哈希表底层的数据结构。
第四部分将详细介绍哈希表的构造算法。
我们将比较常见的哈希函数算法及其特点,然后综述和分析不同碰撞处理算法,并探讨构造算法在不同应用场景中的优化方法。
第五部分将重点解决哈希表冲突的方法。
我们将介绍开放地址法(如线性探测、二次探测等)以及链地址法和拉链法,并讨论其他可能的冲突解决方法。
最后一部分为结论部分,对哈希表的优缺点进行总结,并对哈希表有序性问题、底层数据结构实现、构造算法和冲突解决方法进行总结与展望。
1.3 目的本文旨在通过对哈希表有序性问题、底层数据结构实现、构造算法和冲突解决方法等方面进行深入研究,以期能够更加全面地理解和应用哈希表。
通过本文的阐述,读者将能够了解到不同问题背后所涉及到的相关理论和算法,并能够在实践中灵活应用哈希表,提高数据结构的效率及性能。
2. 哈希表是有序还是无序的2.1 哈希表的定义和功能哈希表(Hash Table)是一种常用的数据结构,用于存储键值对。
hash表
![hash表](https://img.taocdn.com/s3/m/49cfd28dc67da26925c52cc58bd63186bceb926a.png)
hash表哈希表是计算机科学中常见的数据结构,也称为散列表。
哈希表是由一个数组和一个哈希函数组成的数据结构,其中数组的每个元素被称为“哈希桶”,哈希函数用于计算元素索引,这个索引通常称为“哈希码”,该索引用于在数组中定位正确的哈希桶。
哈希表的设计目标是提供一种在常数时间内完成查找、插入和删除操作的数据结构,因此它通常被用作高效的字典数据结构。
哈希表的基本操作包括put(插入)、get(查找)和delete(删除)。
在哈希表中,元素的索引是根据哈希函数的计算结果得出的。
哈希函数是将输入值(元素)映射到哈希码的函数,哈希函数应该满足的性质是它应该是一致的,即对于同一个输入,哈希函数应该总是返回相同的哈希码,在不同的输入之间,哈希函数应该尽可能地让哈希码分布均衡。
同一个哈希码可能是由不同的输入得出的,这种情况在哈希表中称为“哈希冲突”,哈希冲突的解决方法有开放式寻址和链表法。
在开放式寻址中,当一个哈希冲突发生时,它会尝试向后遍历桶数组,选择第一个空桶,并在这个桶中插入元素。
如果该桶被另一个元素占据,就尝试下一个空桶,如果整个数组被遍历完毕,那么原来的插入操作就失败了。
在开放式寻址中,哈希表中的元素被紧密地存储,所以这种方法较适用于内存空间较小的情况,但这种方法的缺点是在高度填满的哈希表中,访问哈希表的效率会显著降低,并且哈希表中的操作也变得非常耗时。
在链表法中,数组每个位置都是一个链表的头结点,当哈希冲突发生时,新的元素会被插入到对应桶中的链表末尾。
链表法是一种天然并行的数据结构,它允许在同一桶内的元素并行地插入和删除,并且支持调整哈希表的负载因子,使哈希表在高度填充时的性能保持稳定。
但是,链表法的缺点是每个元素都需要存储一个指向下一个元素的指针,在大型哈希表中会占用大量的内存空间。
除了以上两种解决哈希冲突的方法,还有一些其他的方法被广泛地应用在哈希表中,比如线性探测和双重哈希等算法。
线性探测是一种开放式寻址算法,它尝试寻找下一个可用的哈希桶作为冲突的解决方法。
第九章哈希表
![第九章哈希表](https://img.taocdn.com/s3/m/dd19bacdda38376baf1fae7e.png)
对增量 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 的顺序表 的顺序表表示之。 以下标为 则查找过程可以简单进行:取给定值 (学号)的后三位,不需要经过比较 不需要经过比较便 不需要经过比较 可直接从顺序表中找到待查关键字。
哈希表通俗讲解
![哈希表通俗讲解](https://img.taocdn.com/s3/m/c53bff7b5b8102d276a20029bd64783e09127df4.png)
哈希表通俗讲解
哈希表,也称为散列表,是一种数据结构,它根据键值(Key value)直接
进行访问。
也就是说,它通过一个映射函数(通常称为哈希函数)将键值映射到表中的一个位置来访问记录,从而加快查找速度。
这个映射函数将每个元素提炼出一个特征值,该特征值用作数组的下标,将元素存储在数组中。
当需要访问元素时,根据这个特征值可以直接定位到指定的下标来访问元素。
在哈希表中,这个特征值就代表着元素所在的地址。
哈希表存储的是由键和值组成的数据。
例如,可以将每个人的性别作为数据存储,其中键为人名,值为对应的性别。
假设将数据存储在6个箱子里面(长度为6的数组)。
如果想要查询Ally的性别,由于不知道Ally的数据
存储在哪个箱子,只能从头开始查询,这个操作叫做“线性查找”。
而哈希表通过哈希函数计算Ally的哈希值,然后对该值进行模运算(mod),得
到的结果是3。
因此可以知道Ally的数据存储在3号箱子中。
查看3号箱
子发现其中的数据的键与Ally一致,于是便取出对应的值,即知道了Ally
的性别是女。
以上内容仅供参考,建议查阅哈希表相关书籍或咨询技术人员以获取更准确的信息。
哈希表的查找
![哈希表的查找](https://img.taocdn.com/s3/m/bd7e5c035901020207409cdb.png)
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
哈希表的概念理解
![哈希表的概念理解](https://img.taocdn.com/s3/m/4e45c577178884868762caaedd3383c4bb4cb43a.png)
哈希表的概念理解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。
哈希表开放定址法
![哈希表开放定址法](https://img.taocdn.com/s3/m/ea37b666dc36a32d7375a417866fb84ae45cc39e.png)
哈希表开放定址法哈希表是一种数据结构,它使用哈希函数将键映射到桶中,以便快速查找、插入和删除值。
开放定址法是解决哈希表冲突的一种方法,当两个或更多的键哈希到同一桶时,通过一定的方式将它们分散到其他桶中。
以下是哈希表开放定址法的一些常见方法:1.线性探测:这是最简单的开放定址法之一。
当发生冲突时,探测器顺序检查下一个桶,直到找到一个可用的桶或回到起始桶。
这种方法的特点是简单,但可能在某些情况下导致数据分布不均匀。
2.二次探测:这种方法与线性探测类似,但在探测下一个桶之前,它使用一个二次函数来计算下一个桶的索引。
这种方法也可以解决冲突,但仍然可能出现数据分布不均匀的情况。
3.双散列:这种方法使用两个不同的哈希函数,当第一个哈希函数产生冲突时,它使用第二个哈希函数来计算下一个桶的索引。
这种方法可以减少数据分布不均匀的情况,但增加了计算的复杂性。
4.开放寻址法:这种方法使用一个固定大小的数组来存储键值对,并在发生冲突时寻找可用的桶。
这种方法包括多种变体,如链地址法和再哈希法。
链地址法将所有具有相同哈希值的键链接到同一链表中,而再哈希法使用另一个哈希函数来计算下一个桶的索引。
在开放定址法的具体实现中,需要考虑一些因素,例如哈希表的负载因子、键值对的数量以及如何选择下一个桶的索引等。
根据具体情况选择合适的开放定址法可以提高哈希表的性能和数据分布的均匀性。
使用开放定址法时需要注意一些问题。
首先,如果哈希函数的质量不高,可能会导致数据分布不均匀,增加冲突的可能性。
在这种情况下,可以考虑使用更好的哈希函数或对键进行重哈希。
其次,如果负载因子过高,即键值对的数量超过了哈希表的大小,则可能会导致哈希表性能下降。
为了解决这个问题,可以增加哈希表的大小或重新哈希键值对。
另外,开放定址法可能会导致“聚集”现象,即具有相同哈希值的键值对聚集在同一链表中,这会影响查询性能。
为了减少聚集现象的影响,可以尝试使用不同的开放定址法或调整链表长度等参数。
- 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)于是本篇米有了哦~。