今天是刷代码随想录的day6。昨天day5元宵节休息日。今天开始了哈希表部分。对于哈希表的内容之前没有怎么学习过,所以今天的刷题主要以学习方法为主。待二刷的时候争取能够手撕代码!
哈希法
首先一个大前提:何时使用哈希法?
当遇到要快速判断一个元素是否出现在集合里时,就要考虑使用哈希法。
当然,有些题目直接暴力解法通过几层for循环遍历也是可以解出来的,但这可能有非常大的时间复杂度。哈希法将需要判断的元素映射入哈希表中,这样就可以向数组中通过下标寻找元素一样快速定位元素,这就使得原本O(n)的时间复杂度变为O(1)。
常见的三种哈希结构为:
数组:当范围可控,数值不大时,优先选用
set(集合):当数值很大或没有数值限制时,或数值分布很分散时
map(映射):当需要存放两个元素时(key,value)
下面的题目中分别用到了这三种不同的哈希结构。
242. 有效的字母异位词
题目
解题思路--哈希法(数组)
判断t与s是否互为异位词,即判断s与t中是否每个字符出现的次数都相同。很明显可以使用哈希法。
这里由于题目中指出字符仅限小写字母a~z,因此一共有26个字符。数值固定,可以选择数组形式的哈希表。
首先,将a~z映射到数组hash[26]中,数组hash[26]中的每个元素初始值为0。其次,遍历s统计s中每个字符出现的次数,通过+1 写入hash。然后,遍历t统计t中每个字符出现的次数,将次数在hash中做减法。最后,遍历hash[26],若全部为0则两者互为异位词,返回true,否则返回false。
代码
class Solution {
public boolean isAnagram(String s, String t) {
int[] hash = new int[26]; //映射a~z连续26个字母
//遍历s,加法统计s中每个字母出现的次数
for(int i =0; i < s.length(); i++){
hash[s.charAt(i) - 'a']++; }
//遍历t,减法减去t中每个字母出现的次数
for(int i =0; i < t.length(); i++){
hash[t.charAt(i) - 'a']--;
}
//hash数组中存在不为0的,则说明s和t不互为字母异位词
/*for(int i =0; i < 26; i++){
if(hash[i] != 0) return false;
}*/
for(int i : hash){
if(i != 0){
return false;
}
}
//若hash中全都为0,则说明s和t互为字母异位词
return true;
//字符串a中:长度为a.lentgh(), 字符串某一位元素charAt()
//由于a~z的ASCII码是是连续的,因此不需要记'a'的ASCII码,用s[i]-'a'即可将a~z对应到0~25
}
}
349. 两个数组的交集
题目
解题思路--哈希法(set)
本题的数值没有限制,不像上一题一样。因此本题选择使用set。
首先,将nums1处理转为hash表set1。然后遍历nums2。查询nums2中的元素是否存在在set1中。如果存在,将set1中的该元素放入另一个hash表result中。
注意,本题要求返回去重的交集。而Java的Set集合存取无序,元素唯一,已经做到了去重。详见Java集合——Set详解。
代码
class Solution {
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> result = new HashSet<>();
//遍历数组1
for(int i : nums1){
set1.add(i);
}
//遍历数组2过程中判断哈希表中是否存在该元素
for(int i : nums2){
if(set1.contains(i)){
result.add(i);
}
}
return result.stream().mapToInt(x -> x).toArray();
}
/*Set存取无序,元素唯一
HashSet在存储元素的过程中首先会去调用元素的hashCode()值,看其哈希值与已经存入HashSet的元素的哈希值是否相同,如果不同 :就直接添加到集合;如果相同 :则继续调用元素的equals() 和哈希值相同的这些元素依次去比较。如果说有返回true的,那就重复不添加;如果说比较结果都说false,那就是不重复就添加。
*/
}
202. 快乐数
题目
解题思路--哈希法(set)
本题判断无限循环是否出现。因此使用哈希法。若出现无限循环不为快乐数,若不出现则肯定是等于1的时候跳出了,则为快乐数。
将每一次计算完每个位置数组的平方和替换掉n存入set。若set中contains这个n了,则说明出现了无限循环,那么就不是快乐数。若这个n=1,则这个数为快乐数。
代码
class Solution {
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;
res += temp * temp;
n = n / 10;
}
return res;
}
}
//cr.代码随想录
注:通过n%10 和n/10可以对一个数的每一位元素进行处理。这个方法可以用在其他题目中。
1.两数之和
题目
解题思路--哈希法(map)
为什么本题用哈希法?
对于每一个x,我们首先查询哈希表中是否存在target-x 。然后将x插入到哈希表中,即可保证不会让x与自己匹配。
为什么用map?
因为我们需要存放两种值,分别是数组nums中的每一个元素和它们的下标。而map中有key和value,可以存放两种元素。
map中key和value分别存放什么?
key存放数组nums中的每一个元素,因为需要查找target-x是否出现过。
value存放数组nums的下标,因为最后需要返回的是下标。
代码
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
for (int i = 0; i < nums.length; ++i) {
//若target-x存在
if (hashtable.containsKey(target - nums[i])) {
//则返回{target-x,x}
return new int[]{hashtable.get(target - nums[i]), i};
}
//若target-x不存在,则将该key、value存入哈希表
hashtable.put(nums[i], i);
}
//若遍历结束都没有返回,则返回空数组
return new int[0];
}
}
作者:LeetCode-Solution
今天的题目更多的是学习方法。二刷的时候最好能做到手撕代码。加油!