HashMap 和 HashSet 区别
如果你看过 HashSet 源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的(HashSet 的源码非常非常少,因为除了 clone()、writeObject()、readObject()是 HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。
HashMap | HashSet |
---|---|
实现了 Map 接口 | 实现 Set 接口 |
存储键值对 | 仅存储对象 |
调用 put()向 map 中添加元素 | 调用 add()方法向 Set 中添加元素 |
HashMap 使用键(Key)计算 hashcode | HashSet 使用成员对象来计算 hashcode 值,对于两个对象来说 hashcode 可能相同,所以equals()方法用来判断对象的相等性 |
补充
使用hashmap提高查询效率,将每一个存入的值计算他独特的hash值并存入哈希表,hash索引是用数据计算出的hash值去除以相应的桶数量去计算hash索引,查找的时候直接查找相应的索引,去对比相应的几个数据就行,效率高
哈希表底层的数据结构
1.7为数组+链表,1.8为数组+(链表+红黑树)
因为链表的储存量最多为8,所以如果有较多的哈希碰撞就会使用红黑树来储存,因为使用二叉树会出现单边很长的情况,退化为链表,所以使用红黑树。但当哈希碰撞在下一次计算变少时,数据量小于6时即会退化为链表,因为红黑树需要左旋,右旋操作浪费资源。另外二次哈希就是为了减少哈希碰撞。
一般直接用于判重,因为他相当于hashmap只有一个键,做不了额外记录
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
// int[] a = new int[10];
// for(int i: nums1){
// if(a[i] == 0)
// a[i]++;
// }
// for(int i: nums2){
// if(a[i] == 1)
// a[i]++;
// }
// Set b = new HashSet<>();
// for(int i=0; i<10; i++){
// if(a[i]>1)
// b.add(i);
// }
// return b.stream().mapToInt(x -> x).toArray();
if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {
return new int[0];
}
Set set1 = new HashSet<>();
Set resSet = new HashSet<>();
//遍历数组1
for (int i : nums1) {
set1.add(i);
}
//遍历数组2的过程中判断哈希表中是否存在该元素
for (int i : nums2) {
if (set1.contains(i)) {
resSet.add(i);
}
}
//set的steam流转数组
return resSet.stream().mapToInt(x -> x).toArray();
}
}
202. 快乐数
这里set的作用就是判断计算有没有进入循环
我写了递归,递归主要是用在链表里,但是原理也就是最后满足某一条件将前面的所有调用用同一方式串起来,所有我类比24. 两两交换链表中的节点 将while写成了递归(判断n是否还能继续取余最后全部相加)
class Solution {
public boolean isHappy(int n) {
Set record = new HashSet<>();
while(n != 1 && !record.contains(n)){
record.add(n);
n = jisuan(n);
}
return n==1;
}
public int jisuan(int n){
if(n>0){
int t = n%10;
return t*t + jisuan(n/10);
}
return 0;
}
}
相对于set来说,数组和map就能在判重的同时记录一些东西,数组使用下标去重、值记录,hashmap使用key去重,value记录
这道题可以用数组也可以用hashmap,都是先放第一串字母进去,再放第二串,key记录单个字符,value记录字符出现的次数 。放好后将第二串字符往里面放并将次数减去,最后值全为0就代表两个词为异位词
class Solution {
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()){
return false;
}
int[] a = new int[26];
for(int i=0; i'a']++;
a[t.charAt(i) - 'a']--;
}
for(int i: a){
if(i != 0)
return false;
}
return true;
}
}
这道题也是同理,但判断的东西不一样,我们只需把参数magazine放进去并记录每个单词出现次数,然后再用ransomNote一个个去找,如果没有或者次数已经为0了则返回false
遍历String:for(char c: magazine.toCharArray())
map.getOrDefault(c, 0):取不到时不返回null而是你给的值
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
Map map = new HashMap<>();
for(char c: magazine.toCharArray()){
//如果有则加一,没有就为1
map.put(c, map.getOrDefault(c, 0) + 1);
}
for(char c: ransomNote.toCharArray()){
if(!map.containsKey(c) || map.get(c) == 0){
return false;
}
map.put(c, map.get(c) - 1);
}
return true;
}
}
相当与转换为两数相加为0,因为只需要记录有多少个结果所以比较简单,如果结果也要输出的话可能需要map套list了
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int res = 0;
Map map = new HashMap<>();
for(int i: nums1){
for(int j: nums2){
map.put(i+j, map.getOrDefault(i+j, 0) + 1);
}
}
for(int i: nums3){
for(int j: nums4){
res += map.getOrDefault(-i-j, 0);
}
}
return res;
}
}
1. 两数之和
这道题可以用两个for 或者hashmap直接判断有没有与target-nums[i]相同的key达到一次循环解决
两种方法用时对比:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map map = new HashMap<>();
for(int i=0; iif(map.containsKey(target-nums[i])){
return new int[] {map.get(target-nums[i]),i};
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
// for(int i = 0; i < nums.length; i++){
// for(int j = i+1; j < nums.length; j++){
// if(nums[i] + nums[j] == target)
// return new int[] {j,i};
// }
// }
// throw new IllegalArgumentException("No two sum solution");
}
}
这两个听着和两数之和没太大区别,但做起来完全不一样,最好的方式就是剪枝、排序、双指针
class Solution {
public List> threeSum(int[] nums) {
List> res = new LinkedList<>();
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++){
if(nums[i] > 0) return res;
if(i > 0 && nums[i] == nums[i-1]) continue;
int left = i+1;
int right = nums.length-1;
while(right > left){
if((nums[i] + nums[left] + nums[right]) > 0) right--;
else if((nums[i] + nums[left] + nums[right]) < 0) left++;
else{
res.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
right--;
left++;
}
}
}
return res;
}
}
18. 四数之和
class Solution {
public List> fourSum(int[] nums, int target) {
List> res = new LinkedList<>();
Arrays.sort(nums);
for(int i = 0;i < nums.length;i++){
// nums[i] > target 直接返回, 剪枝操作
if (nums[i] > 0 && nums[i] > target) {
return res;
}
if (i > 0 && nums[i - 1] == nums[i]) { // 对nums[i]去重
continue;
}
for(int j = i+1;j < nums.length;j++){
if (j > i + 1 && nums[j - 1] == nums[j]) { // 对nums[j]去重
continue;
}
int right = nums.length - 1;
int left = j + 1;
while(right > left){
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if(sum > target) right--;
else if(sum < target) left++;
else {
res.add(Arrays.asList(nums[i], nums[j], 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;
}
}