leetcode刷题打卡(哈希表)

1.有效的字母异位词(简单)

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

示例 1:

输入: s = "anagram", t = "nagaram" 输出: true 示例 2:

输入: s = "rat", t = "car" 输出: false

哈希表

//数组
class Solution {
    public boolean isAnagram(String s, String t) {
        if(s.length()!=t.length())return false;
         int[] arr = new int[128];
         for(char sh : s.toCharArray())arr[sh]++;
         for(char th : t.toCharArray()){
             arr[th]--;
             if(arr[th]<0)return false;
         }
         return true;
    }
}
//Map
class Solution {
    public boolean isAnagram(String s, String t) {
        if(s.length()!=t.length())return false;
        Map<Character,Integer> map = new HashMap<>();
        for(char sh:s.toCharArray())map.put(sh,map.getOrDefault(sh,0)+1);
        for(char th:t.toCharArray()){
            map.put(th,map.getOrDefault(th,0)-1);
            if(map.get(th) < 0)return false;
        }
        return true;
    }
}

2.赎金信(简单)

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

示例 1:

输入:ransomNote = "a", magazine = "b" 输出:false 示例 2:

输入:ransomNote = "aa", magazine = "ab" 输出:false 示例 3:

输入:ransomNote = "aa", magazine = "aab" 输出:true

//同样哈希表数组和map
class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int[] arr = new int[26];
        for(char m:magazine.toCharArray())arr[m-'a']++;
        for(char r:ransomNote.toCharArray()){
            arr[r-'a']--;
            if(arr[r-'a']<0)return false;
        }
        return true;
    }
}

3.字母异位词分组(中等)

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"] 输出: [["bat"],["nat","tan"],["ate","eat","tea"]] 示例 2:

输入: strs = [""] 输出: [[""]] 示例 3:

输入: strs = ["a"] 输出: [["a"]]

//哈希表Map
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String,List<String>> map =new HashMap<>();
        for(String s:strs){
            char[] sh = s.toCharArray();
            Arrays.sort(sh);//排个序,这样字母相同的字符串就相同了
            String str = Arrays.toString(sh);//排完序的转换成字符串
            List<String> strList = map.getOrDefault(str,new ArrayList<String>());//查找是否有该字符串,没有就new一个
            strList.add(s);//把当前的字符串对应的原字符串加入到之前的或者刚new的字符串中
            map.put(str,strList);//hash表存储
        }
        return new ArrayList<List<String>>(map.values());//返回value即可
        
    }
}

4.找到字符串中所有字母异位词(中等)

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

示例 1:

输入: s = "cbaebabacd", p = "abc" 输出: [0,6] 解释: 起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。 起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。 示例 2:

输入: s = "abab", p = "ab" 输出: [0,1,2] 解释: 起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。 起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。 起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。

滑动窗口(类似于之前的覆盖子字符串问题)

(1)先将目标字符串存储到tmp中;

(2)开始滑动:

右边界寻找在tmp中的字符,有的话(即>0)就弹出,并向右继续扩张;扩张时判断大小是否和目标字符串长度一致,一致则可以添加左边界起始点。

没有的话(即=0)则停止扩张,并将左边界收缩,将之前弹出的弹入

收缩:收缩有两种情况右边界字符为陌生字符或者已弹出字符

陌生字符时,需要将左边界收缩到与右边界重合(窗口长度为0)时,右边界才能扩张。因为重合时才能将当前字符弹入,这样右边界才满足扩张条件(>0);

已弹出字符时:将左边界收缩到弹入右边界这个已弹出字符的位置即可,这个时候右边界就满足继续扩张的条件了。

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> res = new ArrayList<Integer>();
        char[] tmp = new char[26];
        for(char ph : p.toCharArray())tmp[ph-'a']++;
        int n = p.length(),m = s.length();
        int start=0,end=0;
        while(end<m){
            if(tmp[s.charAt(end)-'a']>0){
                tmp[s.charAt(end++)-'a']--;//右边界扩张,弹出
                if(end-start==n)res.add(start);//添加起点
            }else tmp[s.charAt(start++)-'a']++;//左边界收缩,弹入
        }
        return res;
         
    }
 
}

5.两个数组的交集(简单)

给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2] 输出:[2] 示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出:[9,4] 解释:[4,9] 也是可通过的

题意:找出两个数组的非重复元素交集。

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        Set<Integer> res = new HashSet<>();
        Set<Integer> set = new HashSet<>();
        for(int num:nums1){//存储任意一个
            set.add(num);
        }
        for(int num:nums2){
            if(set.contains(num))res.add(num);//查找另一个
        }
        int[] result = new int[res.size()];
        int i=0;
        for(int n:res){//返回的数组,Set转数组
            result[i++]=n;
        }
        return result;
    }
}

6.两数之和(简单)

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。 示例 2:

输入:nums = [3,2,4], target = 6 输出:[1,2] 示例 3:

输入:nums = [3,3], target = 6 输出:[0,1]

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> map = new HashMap<>();//为了方便存下标使用Map
        for(int i=0;i<nums.length;i++){
            if(map.containsKey(target-nums[i]))return new int[]{map.get(target-nums[i]),i};//如果包含差值就说明当前与差值下标即为所求
            else map.put(nums[i],i);
        }
        return new int[0];
    }
}

7.四数相加 II(中等)

给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

0 <= i, j, k, l < n nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

示例 1:

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2] 输出:2 解释: 两个元组如下:

  1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0

  2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0 示例 2:

输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0] 输出:1

a+b+c+d=0转换为a+b=-(c+d),使用哈希表存储并查询

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int count=0;
        Map<Integer,Integer> map = new HashMap<>();
        for(int num1:nums1){
            for(int num2:nums2){
                map.put(num1+num2,map.getOrDefault(num1+num2,0)+1);//存储
            }
        }
        for(int num1:nums3){
            for(int num2:nums4){
                if(map.containsKey(-(num1+num2)))count+=map.get(-(num1+num2));//有则累计次数
            }
        }
        return count;
    }
}

8.三数之和(中等)

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]] 示例 2:

输入:nums = [] 输出:[] 示例 3:

输入:nums = [0] 输出:[]

排序+三指针(位移指针,查找双指针)

初始确定长度小于3即不存在。

(1)排序;确定小的在左边,大的在右边,并确定1种终止情况:

当位移指针指向大于0的数时,说明后续都大于0,不存在和为0的三个数。

(2)查找

如果遇到与位移指针所指前一位相邻数(计算过)相同时跳过;为什么不用nums[k]==nums[k+1]呢?因为前一位的必定包括后一位的(后面的是前面的子集),去重操作,能和后一位成立的必定和前一位也成立,所以用后面一位去重

查找双指针分别指向位移指针后面一位和最后一位

和为三个指针指向的数之和

当和<0时,i++,此时可以去重while(nums[i]==nums[++i]&&i<j),不去重也可以,只是所需判断的时间花费的地方不同。

当和>0时,j--,同样去重while(nums[j]==nums[--j]&&i<j)

和==0时,加入结果集合,并继续移动两个指针并去重,这里一定去重,不然会有重复,上面可以不用去重原因是因为还不用加到结果集合中。

两种方法大同小异
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        //排序
        Arrays.sort(nums);
        if(nums.length<3)return new ArrayList<List<Integer>>();
        List<List<Integer>> res= new ArrayList<>();
        for(int k=0;k<nums.length-2;k++){
            if(nums[k]>0)break;
            if(k>0&&nums[k]==nums[k-1])continue;//位移指针去重
            int i=k+1,j=nums.length-1;
            while(i<j){
                int sum=nums[k]+nums[i]+nums[j];
                if(sum==0){
                    res.add(Arrays.asList(nums[k],nums[i],nums[j]));
                    while(i<j&&nums[i]==nums[++i]);//查找左指针去重
                    while(i<j&&nums[j]==nums[--j]);//查找右指针去重
                }else if(sum<0){
                   while(nums[i]==nums[++i]&&i<j);//查找左指针去重
                }else{
                    while(nums[j]==nums[--j]&&i<j);//查找右指针去重
                }
            }
        }
        return res;
    }
}
​
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        //排序
        Arrays.sort(nums);
        if(nums.length<3)return new ArrayList<List<Integer>>();
        List<List<Integer>> res= new ArrayList<>();
        for(int k=0;k<nums.length-2;k++){
            if(nums[k]>0)break;
            if(k>0&&nums[k]==nums[k-1])continue;//位移指针去重
            int i=k+1,j=nums.length-1;
            while(i<j){
                int sum=nums[k]+nums[i]+nums[j];
                if(sum==0){
                    res.add(Arrays.asList(nums[k],nums[i],nums[j]));
                    while(i<j&&nums[i]==nums[i+1])i++;//查找左指针去重
                    while(i<j&&nums[j]==nums[j-1])j--;//查找右指针去重
                    i++;
                    j--;
                }else if(sum<0){
                   i++;
                }else{
                   j--;
                }
            }
        }
        return res;
    }
}

9.四数之和(中等)

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n a、b、c 和 d 互不相同 nums[a] + nums[b] + nums[c] + nums[d] == target 你可以按 任意顺序 返回答案 。

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0 输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]] 示例 2:

输入:nums = [2,2,2,2,2], target = 8 输出:[[2,2,2,2]]

//方法同上一题三数之和一样,在基础之上在嵌套一层位移指针,但是注意目标值为target,非0,所以不需要>0时break,并且内部的位移指针要在上一层的位移指针基础上位移,查找双指针不变。
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        if(nums.length<4)return new ArrayList<>();
        Arrays.sort(nums);
        List<List<Integer>> res =new ArrayList<>();
        for(int h=0;h<nums.length-3;h++){
            if(h>0&&nums[h]==nums[h-1])continue;
            for(int k=h+1;k<nums.length-2;k++){
                if(k>h+1&&nums[k]==nums[k-1])continue;
                int i=k+1,j=nums.length-1;
                while(i<j){
                    int sum=nums[h]+nums[k]+nums[i]+nums[j];
                    if(sum>target)while(i<j&&nums[j]==nums[--j]);
                    else if(sum<target)while(i<j&&nums[i]==nums[++i]);
                    else{
                        res.add(Arrays.asList(nums[h],nums[k],nums[i],nums[j]));
                        while(i<j&&nums[i]==nums[++i]);
                        while(i<j&&nums[j]==nums[--j]);
                    }
                }
            }
        }
        return res;
    }
}
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        if(nums.length<4)return new ArrayList<>();
        Arrays.sort(nums);
        List<List<Integer>> res =new ArrayList<>();
        for(int h=0;h<nums.length-3;h++){
            if(h>0&&nums[h]==nums[h-1])continue;
            for(int k=h+1;k<nums.length-2;k++){
                if(k>h+1&&nums[k]==nums[k-1])continue;
                int i=k+1,j=nums.length-1;
                while(i<j){
                    int sum=nums[h]+nums[k]+nums[i]+nums[j];
                    if(sum>target)j--;
                    else if(sum<target)i++;
                    else{
                        res.add(Arrays.asList(nums[h],nums[k],nums[i],nums[j]));
                        while(i<j&&nums[i]==nums[++i]);
                        while(i<j&&nums[j]==nums[--j]);
                    }
                }
            }
        }
        return res;
    }
}

链接:https://leetcode-cn.com
来源:力扣(LeetCode)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸟养成计划111

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值