赞
踩
当我们想使⽤哈希法来解决问题的时候,我们⼀般会选择如下三种数据结构。
当我们遇到了要快速判断⼀个元素是否出现集合⾥的时候,就要考虑哈希法。如果在做⾯试题⽬的时候遇到需要判断⼀个元素是否出现过的场景也应该第⼀时间想到哈希法!
这一部分的题就是需要注意,如果用到了set,map,在定义的时候需要用包装类,也就是引用数据类型,而不是基本数据类型
例如 int
、double
、char
等。但是 Java 提供了对应的包装类来解决这个问题,例如 Integer
、Double
、Character
等。所以,你可以在泛型中使用这些包装类来代替基本数据类型。
例如,你可以使用 Integer
代替 int
,Double
代替 double
,Character
代替 char
,以及其他包装类来替代其他基本数据类型。
// byte 对应 Byte
// short 对应 Short
// int 对应 Integer
// long 对应 Long
// float 对应 Float
// double 对应 Double
// char 对应 Character
// boolean 对应 Boolean
其实就是不能出现一个字母只在前面出现,但是没在后面那个字符串中。并且需要 s
和 t
中每个字符出现的次数都相同
可以用数组,遍历字符串,然后设一个数组,hash[26],每遍历到一个字母,相应位置加1,之后遍历第二个字符串,遍历到对应字母就去减1,然后看最后这个hash中有无负值
public boolean isAnagram(String s, String t) {
int[] hash = new int[26];
for(int i = 0; i < s.length(); i++){
hash[s.charAt(i) - 'a']++; // 并不需要记住字符a的ASCII,只要求出⼀个相对数值就可以了
}
for(int i = 0; i < t.length(); i++){
hash[t.charAt(i) - 'a']--;
}
for(int i = 0; i < 26; i++){
if(hash[i] != 0)
return false;
}
return true;
}
一个set集合用于存放第一个数组中的元素,另外一个用于在遍历第二个数组的时候,去查找如果有相同元素情况,就存放进去
之后结果数组的大小,就是基于第二个set集合的大小new出来的。并且遍历第二个set集合,将里面的元素放进最终数组里。
public int[] intersection(int[] nums1, int[] nums2) { HashSet<Integer> set = new HashSet<Integer>(); HashSet<Integer> result = new HashSet<Integer>(); for(int i = 0; i < nums1.length; i++){ set.add(nums1[i]); } for(int i = 0; i < nums2.length; i++){ if(set.contains(nums2[i])) result.add(nums2[i]); } int[] res = new int[result.size()]; int n = 0; for(int num: result){ res[n++] = num; } return res; }
这道题需要明确借助map的话,key和value分别存放什么
因为我们是需要基于数组值去查询的,所以key存放的是数组值,value存放的是对应的索引
在遍历数组的时候,只需要向map去查询是否有和⽬前遍历元素匹配的数值,如果有,就找到的匹配对,如果没 有,就把⽬前遍历的元素放进map中,因为map存放的就是我们访问过的元素。
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
int[] result = new int[2];
map.put(nums[0],0);
for(int i = 1; i < nums.length; i++){
if(map.containsKey(target - nums[i])){
result[0] = map.get(target - nums[i]);
result[1] = i;
return result;
}
map.put(nums[i],i);
}
return result;
}
这题和四数之和的区别在于,这是四个数组,所以不需要考虑去重,因为四个数组是独立的,而四数之和问题是针对于一个数组。会可能结果出现重复的
本题的思路:
将四个数组两两划分到一组
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) { HashMap<Integer,Integer> map = new HashMap<Integer, Integer>(); int count = 0; int n; for(int i = 0; i < nums1.length; i++){ for(int j = 0; j < nums2.length; j++){ if(map.get(nums1[i] + nums2[j]) == null) n = 1; else n = map.get(nums1[i] + nums2[j])+1; map.put(nums1[i] + nums2[j],n++); } } for(int i = 0; i < nums3.length; i++){ for(int j = 0; j < nums4.length; j++){ int tmp = 0 - nums3[i] - nums4[j]; if(map.containsKey(tmp)) count += map.get(tmp); } } return count; }
这个题在写代码时候,需要注意,在填充次数的时候,由于Integer不存在0的情况,只是null
这是和int基本数据类型的一大区别,所以我们需要判断他是不是本身不存在,然后赋值
首先想到哈希解决,可以用两个for循环,然后判断出来是不是存在一个key = 【0-a-b】
但是这个就有一个问题,会有重复的三元组,需要去重
因此,这里选择用另外一种方法:双指针法,会更高效一点
由于我们需要返回值,不用返回下标,所以可以提前对数组进行排序
之所以排序,因为我们的思路是让 i 去遍历数组,然后left首先指向【i+1】的位置,right指向最后一个位置
如果nums[ i ] + nums[ left ] + nums[ right ] > 0;这时候如果排过序,可以让right–。就类似这样的操作。并且如果nums[ i ]本身就是大于0的,那就可以不用找了,因为left 和 right只会更大
也需要进行去重:
public List<List<Integer>> threeSum(int[] nums) { int n = nums.length; Arrays.sort(nums); List<List<Integer>> result = new ArrayList<List<Integer>>(); // 找出a + b + c = 0 // a = nums[i], b = nums[left], c = nums[right] for(int i = 0; i < n; i++){ if(nums[i] > 0) // 排序后如果第一个元素已经大于零,无论如何组合都不可能凑成三元组,直接返回结果就可以了 return result; if(i > 0 && nums[i] == nums[i-1]) // 去重a continue; int left = i + 1; int right = n - 1; while(left < right){ if(nums[i] + nums[left] + nums[right] > 0) right--; else if(nums[i] + nums[left] + nums[right] < 0) left++; else{ List<Integer> list = new ArrayList<Integer>(); list.add(nums[i]); list.add(nums[left]); list.add(nums[right]); result.add(list); // 去重逻辑应该放在找到一个三元组之后,对b 和 c去重 一定要用while不是用if!!!!!!! while (right > left && nums[right] == nums[right - 1]) right--; while (right > left && nums[left] == nums[left + 1]) left++; right--; //别忘了上面只是判断,这里才是真正的移动!!!!!! left++; } } } return result; }
和三数之和一样,都是在一个数组上寻找四个元素的和等于target值,并且要求结果里面不能有重复的四元组结合
基本思路:
在三数之和的思路上,外面再加上一层for循环
这里的剪枝和对nums[ k ]的去重稍微有点不一样,因为这次是和target手动输入的值作比较,不像三数之和是单纯的和零做比较
public List<List<Integer>> fourSum(int[] nums, int target) { List<List<Integer>> result = new ArrayList<>(); Arrays.sort(nums); for (int k = 0; k < nums.length; k++) { // nums[i] > target 直接返回, 剪枝操作 if (nums[k] > 0 && nums[k] > target) { return result; } if (k > 0 && nums[k - 1] == nums[k]) { // 对nums[i]去重 continue; } for (int i = k + 1; i < nums.length; i++) { if (i > k + 1 && nums[i - 1] == nums[i]) { // 对nums[j]去重 continue; } int left = i + 1; int right = nums.length - 1; while (right > left) { // nums[k] + nums[i] + nums[left] + nums[right] > target int会溢出 long sum = (long) nums[i] + nums[k] + nums[left] + nums[right]; if (sum > target) { right--; } else if (sum < target) { left++; } else { result.add(Arrays.asList(nums[i], nums[k], nums[left], nums[right])); // 对nums[left]和nums[right]去重 while (right > left && nums[right] == nums[right - 1]) right--; while (right > left && nums[left] == nums[left + 1]) left++; left++; right--; } } } } return result; }
基于代码随想录的学习,同时刷题总结哈希表的常用思路以及一些刷题笔记
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。