代码随想录hash表总结

Java中常见的三种hash结构

  1. 数组(存放在连续内存空间上的相同类型数据的集合
  2. set(仅存储对象)
  3. map(储存键值对)

HashMap 和 HashSet 区别
  如果你看过 HashSet 源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的(HashSet 的源码非常非常少,因为除了 clone()、writeObject()、readObject()是 HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。

HashMap  HashSet
实现了 Map 接口实现 Set 接口
存储键值对 仅存储对象
 
调用 put()向 map 中添加元素调用 add()方法向 Set 中添加元素
HashMap 使用键(Key)计算 hashcodeHashSet 使用成员对象来计算 hashcode 值,对于两个对象来说 hashcode 可能相同,所以equals()方法用来判断对象的相等性  

补充

  使用hashmap提高查询效率,将每一个存入的值计算他独特的hash值并存入哈希表,hash索引是用数据计算出的hash值去除以相应的桶数量去计算hash索引,查找的时候直接查找相应的索引,去对比相应的几个数据就行,效率高

哈希表底层的数据结构

1.7为数组+链表,1.8为数组+(链表+红黑树)

  因为链表的储存量最多为8,所以如果有较多的哈希碰撞就会使用红黑树来储存,因为使用二叉树会出现单边很长的情况,退化为链表,所以使用红黑树。但当哈希碰撞在下一次计算变少时,数据量小于6时即会退化为链表,因为红黑树需要左旋,右旋操作浪费资源。另外二次哈希就是为了减少哈希碰撞。

hashSet

  一般直接用于判重,因为他相当于hashmap只有一个键,做不了额外记录

349. 两个数组的交集
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<Integer> 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<Integer> set1 = new HashSet<>();
        Set<Integer> 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<Integer> 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;
    }
}

hashmap和数组

  相对于set来说,数组和map就能在判重的同时记录一些东西,数组使用下标去重、值记录,hashmap使用key去重,value记录

242. 有效的字母异位词

  这道题可以用数组也可以用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<s.length(); i++){
            a[s.charAt(i) - 'a']++;
            a[t.charAt(i) - 'a']--;
        }
        for(int i: a){
            if(i != 0)
            return false;
        }
        return true;
    }
}
383. 赎金信

  这道题也是同理,但判断的东西不一样,我们只需把参数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<Character,Integer> 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;
    }
}
454. 四数相加 II

  相当与转换为两数相加为0,因为只需要记录有多少个结果所以比较简单,如果结果也要输出的话可能需要map套list了

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int res = 0;
        Map<Integer, Integer> 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<Integer, Integer> map = new HashMap<>();
        for(int i=0; i<nums.length; i++){
            if(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");
    }
}

 三数之和与四数之和

   这两个听着和两数之和没太大区别,但做起来完全不一样,最好的方式就是剪枝、排序、双指针

15. 三数之和
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> 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<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> 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;
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值