哈希表
数组、map以及set
一般哈希碰撞有两种解决方法, 拉链法和线性探测法。
使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题
注意:如果在做面试题目的时候遇到需要判断一个元素是否出现过的场景也应该第一时间想到哈希法!
参考文献: 代码随想录
leetcode 242 有效的字母异位词
利用数组实现哈希表。注意C++中提取字符串的每个字符可以用字符串名[i]的方法,而Java中要用字符串名.charAt(i)的方法。由于数组的大小可控,故可直接用数组下标做哈希映射。
class Solution {
public boolean isAnagram(String s, String t) {
int[] hash = new int[26];
for(int i = 0; i < 26; i++){
hash[i] = 0;
}
for(int i = 0; i < s.length(); i++){
hash[s.charAt(i) - 'a']++;
}
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;
}
}
leetcode 349 两个数组的交集
注意:使用HashSet容器来求解。容器中的类型需要是诸如String、Integer、Object、Date以及Void这些引用类型。最后将结果转换成数组输出,数组的initCapacity可以由容器的.size()方法获取。
import java.io.*;
import java.util.*;
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
//Java中HashSet是基于HashMap实现的,初始容量是16,默认的加载因子是0.75
Set<Integer> result = new HashSet<Integer>();
Set<Integer> nums = new HashSet<Integer>();
for(int i = 0; i < nums1.length; i++){
nums.add(nums1[i]);//不会添加重复的元素
}
for(int i = 0; i < nums2.length; i++){
if(nums.contains(nums2[i])){
result.add(nums2[i]);
}
}
int[] result2 = new int[result.size()];
int j = 0;
for(int i : result){
result2[j] = i;
j++;
}
return result2;
}
}
leetcode 1 两数之和
用HashMap存储已经遍历过的数。其中key存放数值,value存放数值的下标。
注意HashMap中containsKey()方法和get()方法的使用。
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> map = new HashMap<Integer, Integer>();
int[] result = new int[2];
for(int i = 0; i < nums.length; i++){
int j = target - nums[i];
if(map.containsKey(j)){
result[0] = map.get(j);
result[1] = i;
map.put(nums[i],i);//将元素自己也加入到map中
}
map.put(nums[i],i);
}
return result;
}
}
四数相加
不用考虑去重。本题也使用HashMap,其中key存放数值,value存放该数值出现的次数。
注意用Java进行map[a+b]++操作时(将对应数值的出现次数加1),利用变量val赋值时要用++val(具体见代码)。
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
int result = 0;
for(int i = 0; i < nums1.length; i++){
for(int j = 0; j< nums2.length; j++){
if(map.containsKey(nums1[i]+nums2[j])){
int val = map.get(nums1[i]+nums2[j]);
map.put(nums1[i]+nums2[j],++val);//这里时++val
}else{
map.put(nums1[i]+nums2[j],1);//第一次出现,故表示次数的value = 1
}
}
}
for(int i = 0;i < nums3.length;i++){
for(int j = 0;j < nums4.length;j++){
int target = 0 - (nums3[i]+nums4[j]);
if(map.containsKey(target)){
result += map.get(target);
}
}
}
return result;
}
}
leetcode 15 三数之和
不建议用hash法去做,因为去重的细节太复杂。利用双指针法,先将数组升序排序。
对a去重:nums[i] == nums[i - 1]。对b和c的去重一定要放在已经获得一个结果之后的代码中,否则会遗漏{0,0,0,0,0}这种情况。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++){
if(nums[i] > 0){return result;}
//对a去重
if(i > 0 && nums[i] == nums[i - 1]){continue;}
int left = i + 1;
int right = nums.length - 1;
while(left < right){
if(nums[i] + nums[left] + nums[right] < 0){
left++;
}else if(nums[i] + nums[left] + nums[right] > 0){
right--;
}else{
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
//对b和c去重
while(left < right && nums[left] == nums[left + 1]){left++;}
while(left < right && nums[right] == nums[right - 1]){right--;}
left++;
right--;
}
}
}
return result;
}
}
leetcode 18 四数之和
在三数之和的基础之上,外加一层for循环。注意target有可能是负数。故二级剪枝时应该将nums[i]+nums[k]看作一个整体,剪枝的条件是:nums[i]+nums[k] > target && nums[i]+nums[k] > 0 &&target > 0。
class Solution {
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++){
//一级剪枝和去重
if(nums[k] > target && nums[k] > 0){return result;}
if(k > 0 && nums[k] == nums[k - 1]){continue;}
for(int i = k + 1; i < nums.length; i++){
//二级剪枝和去重
// if(nums[k] + nums[i] > target && nums[k] + nums[i] > 0 && target > 0 )
// {return result;}
if(i > k + 1 && nums[i] == nums[i - 1]){continue;}
//定义双指针left和right
int left = i + 1;
int right = nums.length - 1;
//遍历在nums[k]与nums[i]固定的前提下的nums[left]和nums[right]
while(left < right){
if((nums[k] + nums[i] + nums[left] + nums[right]) < target){
left++;
} else if((nums[k] + nums[i] + nums[left] + nums[right]) > target){
right--;
}else{
result.add(Arrays.asList(nums[k], nums[i], nums[left], nums[right]));
while(left < right && nums[left] == nums[left + 1]){left++;}
while(left < right && nums[right] == nums[right - 1]){right--;}
//没有进while循环时也要记得移动left和right指针
left++;
right--;
}
}
}
}
return result;
}
}
leetcode 202 快乐数
根据题意,若不是快乐数,则sum的值会出现循环,每一次都将sum值存入HashSet中,若HashSet包含出现过的sum,则说明不是快乐数。
class Solution {
public boolean isHappy(int n) {
HashSet<Integer> sumSet = new HashSet<Integer>();
int sum = 0;
while(n != 0){
sum = sum + (n % 10) * (n % 10);
n = n / 10;
if(n == 0){
if(sumSet.contains(sum)){
return false;
}else if(sum == 1){
break;
}else{
sumSet.add(sum);
n = sum;
sum = 0;
}
}
}
return true;
}
}
leetcode 383 赎金信
利用有效的字母异位词的思想。
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int[] nums1 = new int[26];
int[] nums2 = new int[26];
char[] chars1 = ransomNote.toCharArray();
char[] chars2 = magazine.toCharArray();
for(int i = 0; i < chars1.length; i++){
nums1[chars1[i] - 'a']++;
}
for(int i = 0; i < chars2.length; i++){
nums2[chars2[i] - 'a']++;
}
for(int i = 0; i < 26; i++){
if(nums2[i] >= nums1[i]){
continue;
}else{
return false;
}
}
return true;
}
}