LeetCode部分习题解答记录-查找

349.两个数组的交集

  • 方法1:使用HashSet
class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        HashSet<Integer> hs = new HashSet<>();
        int[] ans = new int[Math.min(nums1.length, nums2.length)];
        int index = 0;
        for(int i = 0; i < nums1.length;i++){
            hs.add(nums1[i]);
        }
        for(int i = 0 ; i < nums2.length ; i++){
            if(hs.contains(nums2[i])){
                hs.remove(nums2[i]);  //只判断出现一次,因此出现一次后就删除,无需再次统计
                ans[index++] = nums2[i];
            }
        }
        return Arrays.copyOfRange(ans, 0, index);
    }
}

350. 两个数组的交集 II

  • 方法1:使用HashMap,元素出现的次数有意义
class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        int[] ans = new int[Math.max(nums1.length,nums2.length)];
        HashMap<Integer,Integer> hm = new HashMap<>();
        int index = 0;
        for(int i = 0 ; i < nums1.length ;i++){
            int count = hm.containsKey(nums1[i]) ? hm.get(nums1[i]) : 0;
            hm.put(nums1[i], count + 1);
        }

        for(int i = 0 ; i < nums2.length;i++){
        	//用containKey方法会报NullPointerException错误.具体原因疑问
            // int count = hm.containsKey(nums2[i]) ? hm.get(nums1[i]) : 0;
            int count = hm.getOrDefault(nums2[i], 0);
            if(count > 0){
                ans[index++] = nums2[i];
                count--;
                //更新count值或者删除
                if (count > 0) {    
                    hm.put(nums2[i], count);
                } else {
                    hm.remove(nums2[i]);
                }
            } 
         }
        return Arrays.copyOfRange(ans, 0, index);
    }
}

242.有效的字母异位词

  • 方法1:使用HashMap统计各字符出现的次数
class Solution {
     public boolean isAnagram(String s, String t) {
         HashMap<Character,Integer> hm = new HashMap<>();
         //若两字符串长度不相等,必输出false
         if(s.length() != t.length()){
            return false;
         }
         for(int i = 0 ; i < s.length();i ++ ){
             int count = hm.getOrDefault(s.charAt(i),0);
             hm.put(s.charAt(i),count+1);
        }
         for(int i = 0 ; i < t.length(); i++){
             int count = hm.getOrDefault(t.charAt(i),0);
             //若count为0,则说明字符串s中无此字符,必为false
             if(count == 0 ){
                 return false;
             }
             if(count > 0){
                 count--;
                 //及时更新HashMap值
                 if(count > 0){
                     hm.put(t.charAt(i),count);
                }else{
                     hm.remove(t.charAt(i));
                 }
             }
         }
         return true;
     }
}
  • 方法2:利用数组
class Solution {
    public boolean isAnagram(String s , String t){
        if(s.length() != t.length()){
            return false;
        }
        int[] ans = new int[26]; //26个英文字母
        //字符串s出现字符加1,字符串t出现字符减1
        for(int i = 0 ;i < s.length();  i++){
            ans[s.charAt(i) - 'a'] ++;
            ans[t.charAt(i) - 'a'] --;
        }
		//若ans[]中有不为0的数,则说明s与t中的字符出现的次数至少有一个不相等
        for(int i = 0 ;i < ans.length;i++){
            if(ans[i] != 0){
                return false;
            }
        }
        return true;
    }
}

202.快乐数

  • 方法1:
class Solution {
    public boolean isHappy(int n) {
        HashSet<Integer> hs = new HashSet<>();
		//判断条件表明,若还未计算到1时Set中都包括了contains,则说明数值开始了循环
        while(n != 1 && !hs.contains(n)){
            hs.add(n);
            n = add(n);
        }
        return n == 1;
    }

    public int add(int n){
        int sum = 0;
        while(n > 0){
            int number = n % 10;
            n = n / 10;
            sum += Math.pow(number,2);
        }
        return sum;
    }
}

290.单词规律

  • 方法1:注意HashMap的put返回值
class Solution {
    public boolean wordPattern(String pattern, String s) {
        String[] str = s.split(" ");
        if(pattern.length() != str.length){
            return false;
        }

        HashMap<Object,Integer> hm = new HashMap<>();

        for(int i = 0 ; i < str.length ; i ++){
            //HashMap,put返回值问题:若put时HashMap中不存在此键,则返回空,否则更新值,并返回旧值
            Integer v1 = hm.put(pattern.charAt(i),i);
            Integer v2 = hm.put(str[i],i);
            //Integer对象的比较
            if(!Objects.equals(v1,v2)){
                return false;
            }
        }
        return true;
    }
}

205.同构字符串

  • 方法1:

      egg 和 add 同构,就意味着下边的映射成立
      e -> a
      g -> d
      也就是将 egg 的 e 换成 a, g 换成 d, 就变成了 add
    
      同时倒过来也成立
      a -> e
      d -> g
      也就是将 add 的 a 换成 e, d 换成 g, 就变成了 egg
    
      foo 和 bar 不同构,原因就是映射不唯一
      o -> a
      o -> r	
      其中 o 映射到了两个字母
    
class Solution {
    public boolean isIsomorphic(String s, String t) {
        Map<Character,Character> map = new HashMap<>();
        int len = s.length();
        for(int i = 0; i < len; i++){
            char ch1 = s.charAt(i);
            char ch2 = t.charAt(i);
            //建立两个字符的映射关系,如果出现了某个字符判断映射关系是否成立
            if(!map.containsKey(ch1)){
                if(map.containsValue(ch2)) 
                    return false;
                map.put(ch1, ch2);
            } else if(map.get(ch1) != ch2) 
                return false;
        }
        return true;
    }
}
  • 方法2:判断字符首次出现的位置是否相等,实际上还是建立字符之间的映射关系
class Solution {
    public boolean isIsomorphic(String s, String t) {
        int len = s.length();
        char[] ch1 = s.toCharArray();
        char[] ch2 = t.toCharArray();
         //通过判断字母首次出现的位置
        for (int i = 0; i < len; i++) {
            if(s.indexOf(ch1[i]) != t.indexOf(ch2[i])){
                return false;
            }
        }
        return true;
    }
}

451. 根据字符出现的频率排序

  • 方法1:
class Solution {
    public String frequencySort(String s) {
        StringBuffer sb = new StringBuffer();
        HashMap<Character,Integer> hm = new HashMap<>();
        //先统计各字符出现的次数
        for(int i = 0 ; i < s.length();i++){
           int count =  hm.getOrDefault(s.charAt(i),0);
            hm.put(s.charAt(i),count+1);
        }
		//用List来存储Map集合
        List<Map.Entry<Character, Integer>> list = new ArrayList<Map.Entry<Character, Integer>>(hm.entrySet());
        //List排序,重写排序器,根据从大到小排序
        list.sort(new Comparator<Map.Entry<Character, Integer>>() {
            @Override
            public int compare(Map.Entry<Character, Integer> o1, Map.Entry<Character, Integer> o2) {
                return o2.getValue().compareTo(o1.getValue());
            }
        });
        //输出每个字符出现的次数,并添加到sb中,最终转换为字符串输出
        for(int i = 0 ; i < list.size() ; i++){
            int count = list.get(i).getValue();
            while(count > 0){
                sb.append(list.get(i).getKey());
                count--;
            }
        }
        return sb.toString();
    }
}

15.3SUM

  • 分析:若使用暴力法,则是三重循环,时间复杂度为o(n^2)。可先固定一个数,然后在剩余的数中运用三重指针进行寻找另外两个数。同时,先将数据进行排序操作,方便查找。
  • 方法1:排序+双重指针
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> list =  new ArrayList<>();
        if(nums.length <= 2) return list;
        Arrays.sort(nums);
        for(int i = 0 ;i < nums.length -2 ;i++){
            if(nums[i] > 0) break; //可以不加,这句不是很理解
            //例如  -4 ,-1,-1,0,1  在i=1时已经找到,i=2时若不跳出则输出重复
            if(i > 0 && nums[i] == nums[i-1]) continue; 
            int left = i + 1;
            int right = nums.length-1;
            while(left < right){
                int sum = nums[i] + nums[left] + nums[right];
                if(sum == 0){
                    list.add(Arrays.asList(nums[i],nums[left],nums[right]));
                    //现在要增加 left,减小 right,但是不能重复,
                    //比如: [-2, -1, -1, -1, 3, 3, 3], i = 0, left = 1, right = 6, [-2, -1, 3] 的答案加入后,需要排除重复的 -1 和 3
                    left++;
                    right--;
                    //两个while去除重复情况
                    while(left < right && nums[left] == nums[left-1]) left++;
                    while(left < right && nums[right] == nums[right+1]) right--;
                } else if (sum < 0){
                    left++;
                }else{
                    right--;
                }
            }
        }  
        return list;
    }
}

18.四数之和

  • 分析:三数之和的升级版,可先固定两个数,在剩余的数中运用双重指针碰撞来进行寻找。
  • 方法1:简略版本,未进行剪纸与重复判断操作,相对来说运行时间较长,进行了一些重复操作。因此碰上相同的解时,压入List时进行了判断。
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> list = new ArrayList<>();
        Arrays.sort(nums);
        if(nums.length < 4){
            return list;
        }
        for(int i = 0 ; i < nums.length;i++){
            for(int j = i+1; j < nums.length; j++){
                // if(nums[j] == nums[j-1]) continue;
                int left = j+1;
                int right = nums.length -1;
                while(left < right){
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if(sum == target){
						//去重操作,判断是否有重复元素已被压入                        
                        if(list.contains(Arrays.asList(nums[i],nums[j],nums[left],nums[right])) == false){
                             list.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));         
                        }
                        left++;
                        right--;
                        while(left < right && nums[left] == nums[left-1]) left++;
                        while(left < right && nums[right] == nums[right+1]) right--;
                    }else if(sum < target){
                        left++;
                    }else{
                        right--;
                    }
                }
            }
        }
        return list;
    }
}
  • 方法2:加入了去重与剪纸操作,运行时间缩短。四个剪枝操作都去除了一些不可能的情况,例如剪枝操作1:nums[i]+nums[i+1]+nums[i+2]+nums[i+3] > target ,在当前循环中,如这连续的四个数相加大于目标值,因此变化任何数为后面的数都会使和变大,不会出现相等的状况。
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> list = new ArrayList<>();
        int length = nums.length;
        Arrays.sort(nums);
        if(nums.length < 4){
            return list;
        }
        for(int i = 0 ; i < nums.length - 3;i++){
            //去重1 例如[-5,-5, -3,-1, 0, 2, 4, 5] -7
            //假设在i=0,-5时已找到相应的集合,如果不进行判断去重操作,在i=1,-5时必定重复压入
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }

            //剪枝操作
            if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
                break;
            }
            if (nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
                continue;
            }

            for(int j = i+1; j < nums.length-2; j++){
                //去重2 例如[-2, -1, -1 , 1, 1, 2, 2] 0 
                //去重操作1与2必须同时存在,才能保证无重复元素 
                if(nums[j] == nums[j-1] && j > i+1) continue;

                //剪枝
                if (nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
                    break;
                }
                if (nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
                    continue;
                }

                int left = j+1;
                int right = nums.length -1;
                while(left < right){
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if(sum == target){
                        list.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));         
                        left++;
                        right--;
                        while(left < right && nums[left] == nums[left-1]) left++;
                        while(left < right && nums[right] == nums[right+1]) right--;
                    }else if(sum < target){
                        left++;
                    }else{
                        right--;
                    }
                }
            }
        }
        return list;
    }
}

16.最接近的三数之和

  • 方法1:
class Solution {
    public int threeSumClosest(int[] nums, int target) {
         //不能使用Integer.MAX_VALUE,测试用例通过130/131,原因未明白
        // int best = Integer.MAX_VALUE;  
        int best = nums[0] + nums[1] + nums[2];
        Arrays.sort(nums);
        int length = nums.length;
        for(int i = 0 ; i < length; i++){
            if(i > 0 && nums[i] == nums[i-1]) continue;
            int left = i + 1;
            int right = length - 1;
            while(left < right){
                int sum = nums[i] + nums[left] + nums[right];
                // 根据差值的绝对值来更新答案
                if (Math.abs(sum - target) < Math.abs(best - target)) {
                    best = sum;
                }
                if (sum == target) {
                    return target;
                }else if(sum < target){                
                    left++;
                    while(left < right && nums[left] == nums[left-1]) left++;
                }else{
                    right--;
                    while(left < right && nums[right] == nums[right+1]) right--;
                }
            }
        }
        return best;
    }
}

454.四数之和2

  • 分析:若运用暴力法四重循环,则超时。因此先将两个数组的和sum压入Map中,再依次便利另两个数组和,判断-sum是否在Map中
  • 方法1:
class Solution {
    public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
        HashMap<Integer,Integer> hm = new HashMap<>();
        int length = A.length;
        int sum = 0;
        int result = 0;
        for(int i = 0 ; i < length ;i++){
            for(int j = 0 ; j < length ; j++){
                sum = C[i] + D[j];
                int count = hm.getOrDefault(sum,0);
                hm.put(sum,count+1);
            }
        }

         for(int i = 0 ; i < length ;i++){
            for(int j = 0 ; j < length ; j++){
                sum = A[i] + B[j];
                if(hm.containsKey(-sum)){
                    result += hm.get(-sum);
                }
            }
        }
        return result;
    }
}

49.字母异位词分组

  • 分析:字母异位词排序后是相同的,因此使用哈希表根据排序后的字符串为key存储各个字母异位词的list
  • 方法1:
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        HashMap<String,List<String>> hm = new HashMap<>();
        for(String value:strs){
            char[] ch = value.toCharArray​();
            Arrays.sort(ch); //将字符数组排序
            String s = String.valueOf(ch);//将字符数组转换为字符串
            if(!hm.containsKey(s)){  //判断是否存在,不存在则建立映射ArrayList
                hm.put(s,new ArrayList<>());
            }
            //每次都压入,将所有str分组
            hm.get(s).add(value);
        }
        return new ArrayList<>(hm.values());
    }
}

447.回旋镖的数量

  • 分析:运用暴力法三重循环,但是题目中n最大为500,因此时间复杂度为O(N^3)的算法会超出时间范围。我们考虑双重循环,因此对某一固定i,遍历数组求出j、k和i的距离,从而构建距离的键值对。如图所示:
    算法示意图

  • 方法1:

class Solution {
    public int numberOfBoomerangs(int[][] points) {
        int length = points.length;
        int result = 0;
        HashMap<Integer,Integer> hm = new HashMap<>();
        for(int i = 0 ; i < length ; i++){
            for(int j =  0; j < length ; j++){
                if(i == j) continue;
                int distance =  (int)(Math.pow(points[i][0]-points[j][0],2) + Math.pow(points[i][1]-points[j][1],2));
                int count = hm.getOrDefault(distance,0);
                hm.put(distance,count + 1);
            }
            for(Map.Entry<Integer, Integer> entry: hm.entrySet()){
                int count = entry.getValue();
                if(count > 1){
                    // while(count > 1){
                        // i固定,只需要考虑i,k的取值,因此是count * count -1 ,不需要while循环递减
                        result += count * (count - 1);
                        // count--;
                    // }
                }
            }
            hm.clear(); 
        }
        return result;
    }
}

219.重复元素Ⅱ

  • 分析:采用HashSet模拟重复窗口,在集合中,一直都只有k个元素。遍历数组,遇到相同的数则返回true。
  • 方法1:
class Solution {
   public boolean containsNearbyDuplicate(int[] nums, int k) {
        HashSet<Integer> hs = new HashSet<>();
        for(int i = 0 ; i < nums.length ; i++){
            if(hs.contains(nums[i])){
                return true;
            }
            hs.add(nums[i]);
            if(hs.size() > k){
                hs.remove(nums[i-k]);
            }
        }
        return false;
    }
}

220.存在重复元素Ⅲ

  • 分析:与219题类似,都是采用set来模拟滑动窗口,只不过条件变化。在本题中,需要寻找在某一个范围的值,因此需要使用有序集合TreeSet。其中有floor与ceiling方法可以找到<=或者>=某个数的最大或者最小值。由于本题中测试样例可能出现int能表示的最大值,则num[i]+t会出现超出int表示范围的值。因此,需要转换为long类型进行逻辑判断。
  • 方法1:
class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
       TreeSet<Long> ts = new TreeSet<>();
       for(int i = 0 ; i < nums.length ;i++){
           Long number = ts.ceiling((long)nums[i] - (long)t);
           if(number != null && number <= (long)nums[i] + (long)t){
               return true;
           }
           ts.add((long)nums[i]);
           if(ts.size() > k){
               ts.remove((long)nums[i - k]);
           }
       }
       return false;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值