LeetCode 242 有效字的字母异位词
题目链接:242.有效的字母异位词
作为哈希表系列的第一题,此题的目的是让我们明白:数组其实就是一个简单的哈希表
同时也让我们了解用数组做哈希映射时该怎么做
【解题思路】
大体思路:用一个数组记录两个字符串中每个字母出现的次数。
1.定义一个数组record用来记录字符串s里字符出现的次数,大小为26,初始化为0。
之所以定义大小为26,初始化为0,是因为在ASCII码中,字符a到字符z也是26个连续的 数值,我们需要将字符a-z定义为数组的下标,在字符串中,每遍历到一个字符,代表其的下 标中的元素+1,这样我们就能统计出当前字符串每个单词的出现次数了。
2.将字符a到字符z映射到数组的索引下表上,字符a映射为下标0,字符z映射为下标25。
具体做法是:在遍历字符串时,将每个遍历出来的字符减去字符‘a’(因为字符a为a-z的开 头,又因为组成单词的字母只有a-z26个,将每次遍历出来的字符减去字符a,其差就可以当 做数组的下标使用。)
3.遍历字符串s,当出现字符时,相应字符下标位置里的元素+1。
4.遍历字符串t,当出现字符时,相应字符下标位置里的元素-1。
5.检查数组里的元素,如果record数组里有不为0的元素,则说明字符串s和t一定是谁多了或者少了字符,返回false;反之,如果record数组里所有的元素都为0,说明字符串s和t是异位词,返回true。
【代码部分】
java:
public boolean isAnagram(String s, String t) {
int [] record = new int[26];
for(int i = 0; i < s.length(); i++){
record[s.charAt(i) - 'a']++;
}
for(int i = 0; i < t.length(); i++){
record[t.charAt(i) - 'a']--;
}
for(int count:record){
if(count!= 0){
return false;
}
}
return true;
}
LeetCode 349 两个数组的交集
题目链接:349. 两个数组的交集
此题主要要求我们学会使用一种哈希数据结构,该结构在java中为:HashSet,这个数据结构可以解决很多类似的问题。
注意:根据实例可以发现输出结果中的每个元素一定是唯一的,也就是说输出的结果是去重的,同时可以不考虑输出结果的顺序。
【解题思路】
在LeetCode没有更改本题的测试用例(更改之后规定测试用例最大不超过1000)时,此题的数值可以很大,并且可能数值之间的跨度非常大。在此前提下,用数组做该题就会造成空间的极大浪费。
当哈希值比较少、特别分散、跨度非常大的时候,使用数组会造成空间的极大浪费!
此时,应该使用另一种结构体:set。
在java中,关于set提供了两种可用的数据结构:
- TreeSet
- HashSet
TreeSet的底层实现是红黑树,HashSet的底层实现是哈希表,TreeSet是有序的,HashSet是无序的,两者中,HashSet的效率是比较高的,另外,此题并不需要对数据进行排序,因此选择HashSet。
【代码部分】
public int[] intersection(int[] nums1, int[] nums2) {
if(nums1 == null || nums1.length ==0 || nums2 == null || nums2.length == 0){
return new int[0];
}
Set<Integer> set1 = new HashSet<>();
Set<Integer> set2 = new HashSet<>();
//遍历数组1
for(int i : nums1){
set1.add(i);
}
//遍历数组2的过程中判断哈希表中是否存在元素
for(int i : nums2){
if(set1.contains(i)){
set2.add(i);
}
}
return set2.stream().mapToInt(x -> x).toArray();
}
LeetCode 202 快乐数
题目链接:LeetCode202. 快乐数
【题目思路】
根据题意可知:可能会有无限循环,也就是说求和过程中sum可能会重复出现!
因此,我们的解题思路应该是判断sum是否重复出现,重复的话就return false,如果没有重复,那就一直找到sum为止。
当我们遇到要快速判断一个元素是否出现在集合里的时候,优先考虑哈希法!!!
【代码部分】
public boolean isHappy(int n) {
Set<Integer> record = new HashSet<>();
while (n != 1 && !record.contains(n)) {
record.add(n);
n = getNextNumber(n);
}
return n == 1;
}
private int getNextNumber(int n) {
int res = 0;
while (n > 0) {
int temp = n % 10;//将n的个位数取出来
n = n / 10;//将取出来的那一位数从n里去除
res += temp * temp;//将取出来的数进行平方之后累加起来
//重复以上步骤,直到n内没有数可取(也就是n<=0)
}
return res;
}
【疑难点】
注意求和的过程,如果对取数值各个位上的单数操作不熟悉的话,此题会做的比较艰难。
LeetCode 1 两数之和
题目链接:LeetCode1.两数之和
有人相爱,有人夜里开车看海,有人leetcode第一题都做不出来。
因为此题在了解哈希法之前并不算简单。
这道题应该是大部分人梦的开始。
此题的目的是让我们明白什么时候该用map做哈希映射,以及怎么用map做哈希映射。
【解题思路】
再强调一下什么时候使用哈希法:
当我们遇到要快速判断一个元素是否出现在集合里的时候,优先考虑哈希法!!!
此题我们不仅需要知道元素有没有被遍历过,还要知道这个元素对应的下标,需要存放两个值,而map正好有两个值:一个key,一个value。因此,本体使用map正合适。
用数组和set来做哈希法的局限:
- 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
- set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。
在java中,map有三种类型:
- TreeMap:底层使用红黑树实现;key不可重复,效率为O(logn)
- HashMap:底层使用数组+链表+红黑树实现;key不可重复,效率为O(1)
- IdentityHashMap:底层使用哈希表实现;key可重复,效率为O(logn)
在此题中,我们需要保证key不可以重复的前提下,选择更高效的数据结构,因此我们使用HashMap。
接下来我们需要考虑两点:
- map用来做什么
- map中key和value分别表示什么
map用来存放我们访问过的元素,因为遍历数组的时候,我们需要把遍历过的元素及其对应下标记录下来,这样我们才能找到与当前元素匹配的元素。
key表示元素,因为我们要保证元素的唯一性,因此key必须唯一。
因此,value自然用来存放与元素对应的下标。
在遍历数组的时候,只需要向map去查询是否有和目前遍历元素相匹配的数值即可
如果有,即表示配对成功,返回两个元素对应的下标。
如果没有,即表示整个数组里没有两个元素相加等于target的情况。
【代码部分】
java:
public int[] twoSum(int[] nums, int target) {
int [] res = new int[2];//用来存放匹配到的两个元素下标
if(nums == null || nums.length == 0){
return res;
}
Map<Integer,Integer>map =new HashMap<>();
for(int i = 0; i < nums.length ; i++){
int temp = target - nums[i];
if(map.containsKey(temp)){
res[1] = i;//获取当前元素下标
res[0] = map.get(temp);//获取与之匹配的元素在数组中的下标
break;
}
map.put(nums[i],i);//如果没有找到匹配对,则将访问过的元素存入key,下标存入value
}
return res;
}