【Abstract】In order to improve the efficiency of the algorithm, the hash idea is a very useful method in the practice of the algorithm. The reasonable use of hash idea to solve the algorithm problem can effectively help us solve the algorithm problem, especially some problems are very suitable for using hash idea to solve. This article will use some examples on LeetCode to explain and illustrate some practical cases of hashing ideas in algorithm cases.
【摘要】为了提高算法的效率,哈希思想是在算法实践中很有用的的一个方法,合理运用哈希思想来解决算法问题,可以有效的帮助我们解决算法问题,尤其是有些问题非常适合使用哈希思想来解决。本文将借助LeetCode上的一些例子,来讲解和说明哈希思想在算法案例中的一些实践案例。
【关键词】 算法思想 LeetCode 算法效率 算法
一、引言
在日常进行代码编程和实际的工作中,经常会遇到需要设计一些算法的场景,哈希表作为数据结构中常见的一种形式,有很大的用处,合理使用哈希表可以有效的帮助到我们来设计更有效率的算法。一般地,在很多算法设计的时候,都会遇到动态集合的需求,需要支持插入、查找、删除的操作,使用哈希表来构建,会是一种非常有效的思路。哈希思想就是使用哈希表的手段,来解决一些实际的问题,即本文主要通过一些实践例子来试图讲解哈希思想的运用。
二、什么是哈希思想
哈希表也叫散列表,由三部分组成,一部分是关键字,也叫直接寻址表,一部分是数组,也叫哈希表或者散列表,一部分是哈希函数,也叫散列函数,可以通过关键字通过散列函数在数组中查找数据,这样理想的情况就是查找时间O(1),最差的情况会出现整个列表全都查找一遍,时间是O(n)。但是这种最差的情况一般不会出现,因为哈希表的关键字在构建的时候一般尽量选择具有唯一性的值,比如使用哈希散列算法计算出来的哈希值。哈希思想简单总结来说,就是构建一个关键字函数,然后通过哈希函数来与数组部分相关联就可以查找数据了。
当关键字数量比较小的时候,可以使用直接寻址法,把全部的关键字都构建出来,然后建立查找关系就可以了。当关键字比较多的时候,可以使用开放寻址法,按照需要,动态构建相关的关键字集合,然后再建立增删改查的关系就可以了。构建完全哈希表,静态存储全部关键字,可以在完全哈希表中最坏情况下完成关键字查找,这种在有些情况下也是合适的做法,具体采用何种做法,视具体情况而定。
当关键字无法保证覆盖全部的数据时,就会出现关键字冲突,导致一个关键字对应多个数据,采用的方法很多,比如对于冲突的数据采用链表的方式,或者红黑树来建立查找关系,通过一一查找来寻找数据,这种方法好处是基本可以覆盖所有的数据,坏处是,在冲突的关键字上会存在数据堆积现象,可能需要把冲突的数据全部查找一遍才可以,而且还需要另外开辟一个空间来存储数据,增加空间消耗。当然还有别的办法,比如开放寻址法,当关键字冲突的时候,使用在散列法,使用该关键字再生成一个新的关键字,如果还冲突就重复再用新生成的这个关键字生成一个关键字,直到不重复为止,这种方法的优点是,保证可以放下全部的数据,节省空间,这种方法的缺点是不能真正地删除数据,因为后面新生成的关键字是可能和之前删除的关键字相关的,只能标记一下已经删除了。在开放寻址法中重新生成冲突的关键字的方法,还有链表法,还有比如线性探查法,直接依次探查下一个关键字就可以了,还有比如二次探查、双重散列、建立公共溢出区等等方法。
随着哈希表数据的增加,可以使用装填因子也叫装载因子来表示当前数据的填满程度,装填因子的计算方法是装入哈希表中的关键字个数与当前哈希表总的长度的比值,当装填因子越大的时候,填充的数据越多,空间利用率越高,但是关键字冲突的可能性就会增加,查找数据的成本可能就会增加。当装填因子越小的时候,填充的数据越少,空间利用率就越低,关键字冲突的概率就会减少,查找数据的成本越低。往往在设置装填因子的时候,就需要根据实际情况做一些取舍和平衡来冲突的概率和空间利用率,然后根据装填因子来决定是否增加哈希表的长度还是什么别的处理。
文本的代码实现都是使用Java实现。
三、删除字符使频率相同
题目描述:
给你一个下标从0开始的字符串word,字符串只包含小写英文字母。你需要选择一个下标并删除下标处的字符,使得word中剩余每个字母出现频率相同。如果删除一个字母后,并且恰好只删除一个字母,word中剩余所有字母的出现频率都相同,那么返回true,否则返回false。比如输入的字符串是
aaccc,'c’的数量是3,数量减1,就可以让’a’和’c’的数量一样,所以返回tue。1
首先分析下该问题,首先我们需要遍历一遍字符串,首先统计出所有字母的出现频率。英文字母有26,且不考虑大小写,只有小写字母,那么我们就可以构建一个哈希表来记录字母的出现频率,表的长度最大值也就是26了,因为关键字数量很小,所以可以首先初始化出一个26长度的数组,也就是哈希表,然后再去遍历整个字符串,按照26个字母的顺序,来记录频率数据,所以其实这就相当于建立一个字母和频率一一对应关系的字典,这样就可以节约内存空间来避免保存所有的字母,因为数组可以按照字母顺序直接去读取,所以还可以节省插入时的时间消耗。
然后就可以来处理上一步得到的数组了。首先如果得到的所有字母的最大数量就是1,也就是说字符串里所有的字母都不相同,那么可以直接返回为true,因为任何一个数量减1之后都可以符合条件。然后遍历一遍这个数组,可以得到数量的最大值(下方代码中的max,代表出现数量最多的次数是多少)和最小值(下方代码中的min,代表出现数量最少的次数是多少),还可以得到最大数量出现的次数(下方代码中的countMax,代表有多少种字母是这个最大数量max的),和最小数量出现的次数(下方代码中的countMin,代表有多少种字母是这个最小数量min的)。如果判断最大数量出现的次数等于全部的字符长度,那就代表该字符串全部的字母都相同,除非该字符串word的长度小于等于2可以返回为true,否则是返回false。如果最大数量只出现了一次,也就是只有一种字母数量最多,那么最大的数量减去最小的数量,差值为1或者0,比如abcc、abb、cdd、ab、ef这样的字符串,这些都是可以返回为true的,如果是abccc、abbccc、abbb这样的字符串就该返回false。如果最大数量只出现了一次,也就是只有一种字母数量最多,并且其他的字母都是1个,比如accccc、bffff这样的字符串,这些都是返回的true,除此之外的其他的情况都是false。如果最大数量出现了多个,但是最小数量的只出现了一个,并且这两种加起来的数量刚好等于字符串的长度,就代表,比如abbbccc、effgg这样的字符串,这些都应该返回true,除此之外的其他情况都是false,比如abbcccfff这样的字符串就该是false。
实现的代码如下:
class Solution {
public boolean equalFrequency(String word) {
int[] hash = new int[26];
int max = 0;
for (char c : word.toCharArray()) {
hash[c - 'a']++;
max = Math.max(hash[c - 'a'], max);
}
if (max == 1) return true;
int countMax = 0, countMin = 0, min = max;
for (Integer value : hash) {
if (value == 0) continue;
if (value < min) {
countMin = 1;
} else if (value == min) {
countMin++;
}
min = Math.min(value, min);
if (value == max) {
countMax++;
}
}
if (countMax == word.length()) return word.length() <= 2;
if (countMax == 1) {
return max - min == 1 || max - min == 0 || (countMin == 1

本文介绍哈希思想在算法中的应用,阐述哈希表组成、查找原理及关键字冲突处理方法。借助LeetCode上的题目,如删除字符使频率相同、串联所有单词的子串、直线上最多的点数,讲解如何运用哈希思想设计高效算法,还提及哈希表动态扩容等问题。

3015

被折叠的 条评论
为什么被折叠?



