哈希表刷题总结

哈希表刷题总结

基础知识

哈希表提供了一个操作。fun(element) = index。它提供了一个操作,使得可以直接通过元素本身查找到唯一的索引。哈希表解决的问题。一般哈希表都是用来快速判断一个元素是否出现在集合里。

哈希函数就是做这个映射的。
通过HashCode(element) % tablesize 得出Index。

但是不好的哈希函数,或者相对于元素数量,表太小会带来哈希碰撞。这个时候可以用线性探测法(就是再通过一个函数映射到其他位置),或者拉链法(List这个形式)

242 有效的字母异位词
一开始我的思路是,维护两个HashMap。里面装有。然后遍历两个HashMap对对比练的value值是否相等。这样带来的问题就是简单的一个任务但是消耗的内粗暴啥的却不小。有更简单的方法可以实现哈希表的问题。

    public boolean isAnagram(String s, String t) {
        char[] chars = s.toCharArray();
        char[] chars1 = t.toCharArray();
        
        int[] res = new int[26];
        
        for (int i = 0;i<chars.length;i++)
            res[chars[i] - 'a']++;
        
        for (int j = 0;j<chars1.length;j++)
            res[chars1[j] - 'a']--;
        
        for (int m = 0;m<26;m++){
            if (res[m] != 0)
                return false;
        }
        return true;
    }

我们要理解哈希表的本质是,建立元素和索引的一对一映射关系。在这里,元素是char!而英文字母是26位的,只要维护一个长度为26的数组就能建立一对一的映射了。

383 赎金信
和上面那道题其实是一个概念,只不过判定条件变成击穿了。

    public boolean canConstruct(String ransomNote, String magazine) {
        char[] r_array = ransomNote.toCharArray();
        char[] m_array = magazine.toCharArray();
        int[] res = new int[26];
        for (int i = 0;i<r_array.length;i++){
            res[r_array[i] - 'a']--;
        }
        for (int j = 0;j<m_array.length;j++){
            res[m_array[j] - 'a']++;
        }
        for (int m = 0;m<26;m++){
            if (res[m] < 0) return false;
        }
        return true;

    }

49 字母异位词分组

    char[] s_array;
    char[] t_array;
    int[] res;
    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> list = new LinkedList<List<String>>();
        List<String> temp;
        boolean[] visited = new boolean[strs.length];
        for (int i = 0;i<strs.length;i++){
            if (visited[i]) continue;
            visited[i] = true;
            temp = new LinkedList<String>();
            temp.add(strs[i]);
            for (int j = i+1;j<strs.length;j++){
                if (visited[j] || !fun(strs[i],strs[j])) continue;
                visited[j] = true;
                temp.add(strs[j]);
            }
            list.add(temp);
        }
        return list;
    }
    public boolean fun(String s,String t){
        s_array = s.toCharArray();
        t_array = t.toCharArray();
        res = new int[26];;

        for (int i = 0;i<s_array.length;i++)
            res[s_array[i] - 'a']++;
        for (int j = 0;j<t_array.length;j++)
            res[t_array[j] - 'a']--;
        for (int m = 0;m<26;m++){
            if (res[m] != 0) return false;
        }
        return true;
    }

这是我给出的解法。大体思路就是判断两个字符串是不是符合要求,符合要求就加入。但是现实时间太长了。

首先在这里我们介绍另外一种有效的字母异位词的判定手段。如果一个字母是互为异位词,那么这个String转换为字符数组的时候,经过排序,应该得到相同的字母顺序。可以使用Arrays.sort进行排序。

    public boolean isAnagram(String s, String t) {
        char[] array_1 = s.toCharArray();
        char[] array_2 = t.toCharArray();
        Arrays.sort(array_1);
        Arrays.sort(array_2);

        String s_1 = new String(array_1);
        String s_2 = new String(array_2);

        System.out.println(s_1);
        System.out.println(s_2);

        if(s_1.equals(s_2)) return true;
        return false;
    }

经过这样排序,我们可以解除这道题。
首先判定是否为异位词,并且以这个排序号的异位词重新创建key
String key = new String(char[])这是可行的
再通过这个key放入map中,针对这个map取值,如果之前有list存入就加入新的节点,如果没有就创建新的list,这里的算法复杂度是O(n),这就是哈希表的好处,当我们map.get()这个算法的复杂度是1,这是最牛的地方。好好体会

    public boolean isAnagram(String s, String t) {
        char[] array_1 = s.toCharArray();
        char[] array_2 = t.toCharArray();
        Arrays.sort(array_1);
        Arrays.sort(array_2);

        String s_1 = new String(array_1);
        String s_2 = new String(array_2);

        System.out.println(s_1);
        System.out.println(s_2);

        if(s_1.equals(s_2)) return true;
        return false;
    }

349 两个数组的交集

    public int[] intersection(int[] nums1, int[] nums2) {
        HashSet<Integer> nums1_set = new HashSet<>();
        HashSet<Integer> nums2_set = new HashSet<>();
        HashSet<Integer> res = new HashSet<>();
        for (int num:nums1)
            nums1_set.add(num);
        for (int num:nums2)
            nums2_set.add(num);
        for (int num:nums1_set){
            if (nums2_set.contains(num))
                res.add(num);
        }
        int[] fin = new int[res.size()];
        int category = 0;
        for (int num:res)
            fin[category++] = num;
        return fin;
    }

这道题的解法就很多
两个大思路
第一个for for嵌套循环肯定能搞定,相当于暴力解法了属于是
第二个就是用两个HashSet进行比较,用Hashmap也可以,就是最后需要重新定义一个数组重新赋值有点丑这个代码写的。

快乐数

    public boolean isHappy(int n) {
        HashSet<Integer> set = new HashSet<>();
        while (n != 1 && set.add(n)) n = fun(n);
        if (n == 1) return true;
        return false;
    }
    public int fun(int n){
        int sum = 0;
        while (n > 0){
            int i = n % 10;
            n /= 10;

            sum+= i * i;
        }
        return sum;
    }

这道题没啥难度,只要get到无限循环会出现重复的数值即可。

1 两数之和

    public int[] twoSum(int[] nums, int target) {
        int[] res = new int[2];
        for (int i = 0;i<nums.length;i++){
            res[0] = i;
            for (int j = i+1;j<nums.length;j++){
                if (nums[i] + nums[j] == target){
                    res[1] = j;
                    return res;
                }
            }
        }
        return null;
    }

最简单的思路,就是两个for你总能get到真正的,而且他的数据给的很好。但是希望由这道题get到哈希表真正有有意思的地方。

    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer,Integer> map = new HashMap<>();
        for (int i = 0;i<nums.length;i++){
            if (map.containsKey(nums[i])){
                return new int[]{map.get(nums[i]),i};
            }
            map.put(target - nums[i],i);
        }
        return null;
    }

注意,在这里时间复杂度是O(n),这个是很关键的。i + j = target的问题转换成 j = target - i的问题。
遇到几个数的和的问题。都可以采用这个思路!

第454题.四数相加II
这道题的答案写完就是一种我曹的感觉

    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        HashMap<Integer,Integer> map_1_2 = new HashMap<>();
        
        for (int i = 0;i<nums1.length;i++){
            for (int j = 0;j<nums2.length;j++){
                Integer num = map_1_2.getOrDefault(-nums1[i] - nums2[j], 0) + 1;
                map_1_2.put(-nums1[i]-nums2[j],num);
            }
        }
        int sum = 0;
        for (int m = 0;m<nums3.length;m++){
            for (int n = 0;n<nums4.length;n++){
                if (map_1_2.containsKey(nums3[m]+nums4[n])) sum += map_1_2.get(nums3[m]+nums4[n]);
            }
        }
        return sum;
    }

首先这道题的O(n3)肯定是能解出来,套入3个for,但是就麻烦了。
15 三数之和
这道题真心杀了我好长好长时间,主要就是,答案中,并不可以包含重复的三元组这个条件非常的棘手

    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 (left < right){
                if (nums[i]+nums[left]+nums[right]>0 ){
                    //说明此时值大了right --
                    right--;
                }else if (nums[i]+nums[left]+nums[right]<0){
                    //说明此时值小了left++
                    left++;
                }else{
                    //此时相等
                    List<Integer> temp = new LinkedList<>();
                    temp.add(nums[i]);
                    temp.add(nums[left]);
                    temp.add(nums[right]);
                    res.add(temp);
                    while (left < right && nums[left] == nums[left+1]) left++;
                    while (left < right && nums[right] == nums[right-1]) right--;
                    right--;
                    left++;
                }
            }
        }
        return res;
    }

在A数组的题目中双指针yyds,希望不要忘记。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值