哈希表是用来快速判断一个元素是否出现在集合里。
常用的有三种结构:
- 数组
- set
- Map
具体使用哪一种需要根据实际情况判断。
第一类:使用数组作为hash表
题目链接: 242. 有效的字母异位词
因为本题在题目说明中,限定字符的种类仅为小写字母,种类数是固定的,可以使用数组来作为map
class Solution {
public boolean isAnagram(String s, String t) {
int[] map = new int[26];
//遍历s字符串,记录每个字符出现的次数
for(int i = 0;i < s.length();i++){
map[s.charAt(i)-'a']++;
}
//判断t中的每个字符出现的次数,这里使用减的操作就只需要使用一个map了
for(int i = 0;i < t.length();i++){
map[t.charAt(i)-'a']--;
}
//如果不相同,则不是异位词
for(int i = 0;i < map.length;i++){
if(map[i] != 0 ){
return false;
}
}
return true;
}
}
题目链接:383.赎金信
因为本题在题目说明中,限定字符的种类仅为小写字母,种类数是固定的,可以使用数组来作为map
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
//定义一个数组作为哈希表
int[] map = new int[26];
//统计magazine中每个字符出现的次数
for(int i = 0;i < magazine.length();i++){
map[magazine.charAt(i)-'a']++;
}
//统计ransomNote中每个字符出现的次数
for(int i = 0;i < ransomNote.length();i++){
map[ransomNote.charAt(i)-'a']--;
//说明ransomNote中出现了magazine中没有的字符,或者次数已经超过了magazine中出现的次数
if(map[ransomNote.charAt(i)-'a'] < 0){
return false;
}
}
return true;
}
}
第二类:使用set作为哈希表
题目链接: 349 两个数组的交集
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
//首先判断两个数组的长度,先统一换为num1 < num2
if(nums1.length > nums2.length){
return intersection(nums2,nums1);
}
// 因为题目中要求输出结果中的每一个元素一定是唯一的,所有使用set集合
Set<Integer> set = new HashSet<>();
//记录num1中出现的数字
for(int num : nums1){
set.add(num);
}
//定义结果数组,这种方式还是会存在重复元素
// int[] res = new int[nums1.length];
// int index = 0;
// //判断num2中的元素是否在集合中出现
// for(int num : nums2){
// if(set.contains(num)){
// res[index++] = num;
// }
// }
// return Arrays.copyOf(res,index);
Set<Integer> set2 = new HashSet<>();
for(int num : nums2){
if(set.contains(num)){
set2.add(num);
}
}
//这种方式也可以使用set直接转为数组进行简化
// int[] res = new int[set2.size()];
// int i = 0;
// for(int num : set2){
// res[i++] = num;
// }
return set2.stream().mapToInt(x->x).toArray();
}
}
题目链接:202.快乐数
class Solution {
public boolean isHappy(int n) {
//如果出现重复的数字那也就是进入无限循环了,直接返回false就可
Set<Integer> set = new HashSet<>();
n = getNext(n);
while(!set.contains(n) && n != 1){
set.add(n);
n = getNext(n);
}
// if(n == 1){
// return true;
// }else{
// return false;
// }
//上述代码可以简化为以下代码
return n == 1;
}
//统计每个位置上的数字的平方和
public int getNext(int n){
int res = 0;
while(n > 0){
res += (n % 10)*(n%10);
n = n / 10;
}
return res;
}
}
map作为哈希表
题目链接:1 两数之和
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map = new HashMap<>();
int[] res = new int[2];
for(int i = 0;i < nums.length;i++){
//使用map判断之前是否出现过和当前数字相加为target的数字和下标
int tmp = target - nums[i];
if(map.containsKey(tmp)){
res[0] = map.get(tmp);
res[1] = i;
}
map.put(nums[i],i);
}
return res;
}
}
题目链接:454.四数相加II
这道题与两数之和的思路是一样的,不过是map里存储的是num1与num2中元素之和以及出现的次数。
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer,Integer> map = new HashMap<>();
int count = 0;
for(int a : nums1){
for(int b : nums2){
int sum = a + b;
map.put(sum,map.getOrDefault(sum,0)+1);
}
}
for(int c : nums3){
for(int d : nums4){
int target = 0 - c - d;
if(map.containsKey(target)){
count += map.get(target);
}
}
}
return count;
}
}
题目链接:18. 四数之和
关键差别是上一个题目为四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑重复问题,而18. 四数之和一个数组(集合)里找到和为0的组合,需要考虑去重的问题,所以使用双指针法来做更加合适!!!
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
//建立一个结果数组
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
//本题使用双指针法来做更加合适
for(int i = 0;i < nums.length;i++){
//剪枝
if(nums[i] > target && nums[i] >= 0){
break;
}
//去重
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
for(int k = i+1;k < nums.length;k++){
if(nums[k] + nums[i] > target && nums[k] + nums[i] >= 0){
break;
}
if(k > i+1 && nums[k] == nums[k-1]){
continue;
}
int left = k + 1,right = nums.length-1;
while(right > left){
int sum = nums[i] + nums[k] + nums[left] + nums[right];
if(sum > target) right--;
else if(sum < target) left++;
else{
res.add(Arrays.asList(nums[i],nums[k],nums[left],nums[right]));
//去重
while(right > left && nums[right] == nums[right-1]) right--;
while(right > left && nums[left] == nums[left + 1]) left++;
right--;
left++;
}
}
}
}
return res;
}
}
题目链接:15 三数之和
本题和18四数之和基本是一致的。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
if(nums[0] > 0){
return res;
}
for(int i = 0;i < nums.length;i++){
//去重
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
int left = i + 1,right = nums.length - 1;
while(right > left){
int sum = nums[i] + nums[left] + nums[right];
if(sum > 0 ) right--;
else if(sum < 0) left++;
else{
res.add(Arrays.asList(nums[i],nums[left],nums[right]));
while(right > left && nums[right] == nums[right-1]) right--;
while(right > left && nums[left] == nums[left+1] ) left++;
right--;
left++;
}
}
}
return res;
}
}