代码随想录算法训练营第4天 | 哈希表部分:242.有效的字母异位词, 349. 两个数组的交集, 202. 快乐数, 1. 两数之和
哈希表部分
使用O(1)的时间复杂度定位到元素,根据hash函数和冲突解决方法将一组关键字映射到一组有限的地址空间
哈希冲突:不同的关键字经过哈希函数的计算之后得到的地址相同,产生冲突
哈希冲突的解决方法:
线性探测法:从发生冲突的位置开始,依次判断下一个单元是否是空的,如果空则插入,否则继续判断下一个单元
平方探测法:在发生冲突的位置依次加上12 ,22,直到找到空闲的位置
双重散列探测:前一个函数使用取余法取得散列地址,后一个同样,只不过后面的作为探查序列的增量
伪随机探查:建立一个伪随机函数作为探查序列
242. 有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词
思路:将数据作为k,数据出现的次数作为v保存在Hashmap中,分别对字符串s和进行遍历,最后遍历k的同时进行比较
public boolean isAnagram(String s, String t) {
Map<Character,Integer> map1=new HashMap<>();
Map<Character,Integer> map2=new HashMap<>();
if(s.length()!=t.length()){
return false;
}
for(int i=0;i<s.length();i++){
if(map1.containsKey(s.charAt(i))){
map1.put(s.charAt(i),map1.get(s.charAt(i))+1);
}else{
map1.put(s.charAt(i),1);
}
if(map2.containsKey(t.charAt(i))){
map2.put(t.charAt(i),map2.get(t.charAt(i))+1);
}else{
map2.put(t.charAt(i),1);
}
}
for(int i=0;i<t.length();i++){
/* int x=map1.get(t.charAt(i));
int y=map2.get(t.charAt(i));
if(x!=y){
return false;
}*/
if(map1.get(t.charAt(i))==null){
return false;
}
//补充:== b.equals(a)的使用区间
if(!map1.get(t.charAt(i)).equals(map2.get(t.charAt(i)))){
return false;
}
}
return true;
}
补充:== equals的区别
本道题目中是map中get方法的返回值类型是value的类型(Interger)
Integer在-128-127之间可以使用==比较,不在这个范围的值都需要equals进行比较
349. 两个数组的交集
给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
思路:将数据保存为key,数据出现的次数保存为value,对nums1遍历时加1,对nums2遍历时-1,最终判断key对应的value值是否为0。
这样想的思路存在问题:如果nums1中有2个4,nums2中只有一个4,事实上,两者是存在交集的,但是根据上面的判断,value此时不为0;
思路改进:
首先对数据进行去重,这样就能保证交集中key对应的value的值为0(看答案)
思路是对的,使用将数据保存在HashSet中
??这边本来想着是不是map中的key的对应的默认值为0?map中有没有默认值
补充:hashmap中默认的部分时装填因子0.75,容量大小是16,并没有我认为的一个默认的键值对,只有在key存在的情况下,如果没给value赋值的话,会设置为一个默认值
补充:如何将set集合转换为int[ ]数组(老大难问题)
调用set1.toArray()函数仅仅转换为Integer[ ]集合
方法1:使用迭代器遍历,将集合中的每一个Integer转换为int (a.intValue())
方法2:resSet.stream().mapToInt(x -> x).toArray();
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> set1=new HashSet<>();
Set<Integer> resSet=new HashSet<>();
int len=Math.min(nums1.length,nums2.length);
//int[] res=new int[len];
for(int i=0;i<nums1.length;i++){
if(!set1.contains(nums1[i])){
set1.add(nums1[i]);
}
}
//遍历数组2的过程中,判断该元素在数组1中是否出现过,出现过的话再加入结果集中,结果集也是一个HashSet类型的集合
for(int j=0;j<nums2.length;j++){
if(set1.contains(nums2[j])){
resSet.add(nums2[j]);
}
}
return resSet.stream().mapToInt(x -> x).toArray();
//写代码中遇到的思路:直接遍历数组2,最后进行一个遍历对set1,set1的结果进行整合,遇到了问题
//如果再进行一遍遍历的话,感觉无论使用哪一个数组的长度都不对
202. 快乐数
编写一个算法来判断一个数 n 是不是快乐数。「快乐数」 定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1,那么这个数就是快乐数。如果 n 是 快乐数 就返回 true ;不是,则返回 false
思路:就是按照题目给的公式不断地判断循环,然后去判断,但是这样写的话,遇到不是快乐数的n,循环不会停止
答案:
将每个位置上的平方和sum保存在set中,这样就不需要再重复运算,查找的时间复杂度O(1)
public boolean isHappy(int n) {
Set<Integer> set1=new HashSet<>();
//主要是对重复元素的去除
//while(n>1 && !set1.contains(n))也可以
while(n!=1 && !set1.contains(n)){//更准确的表达题目的要求
int sums=0;
set1.add(n);
while(n>0){//注意
sums +=(n%10)*(n%10);
n =n/10;
}
n=sums;
}
return n==1;
}
1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案
思路:暴力法O(n2)
思路2:map实现,一次遍历,如果target-当前位置处的数组元素在map中出现,立刻结束
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++){
if(map.containsKey(target-nums[i])){
res[0]=i;
res[1]=map.get(target-nums[i]).intValue();//转换
}else{
map.put(nums[i],i);
}
}
return res;
}
总结:
HashSet
==和equals的区别
集合转换成int[]数组
`