代码随想录_哈希表

代码随想录03

哈希表

优势 ?

  • 快速判断一个数是否在哈希表内

如何选择数组, set, map ?

  • 数组: 数值限定大小
  • set: 没有限定数值大小
  • map: 不仅要判断n是否存在而且还要记录n的下标位置

242.有效的字母异位词

242. 有效的字母异位词

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

示例 1: 输入: s = “anagram”, t = “nagaram” 输出: true

示例 2: 输入: s = “rat”, t = “car” 输出: false

说明: 你可以假设字符串只包含小写字母。

注: 字母异位词, 两个字符串含有的字母的种类和个数都相同.

**思路: ** 哈希表(数组), 根据ascii码找下标

**代码: **

class Solution {
    public boolean isAnagram(String s, String t) {
        int[] arr = new int[26];
        // 1. 统计s的字符
        for(int i = 0;i < s.length();i++) arr[s.charAt(i) - 'a']++;
        
        // 2. 减去t的字符
        for(int i = 0;i < t.length();i++) arr[t.charAt(i) - 'a']--;
        
        // 3. 判断arr是否全为0
        for(int i = 0;i < arr.length;i++) {
            if(arr[i] != 0) return false;
        }

        return true;
    }
}
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

383.赎金信

383. 赎金信

给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。

(题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。杂志字符串中的每个字符只能在赎金信字符串中使用一次。)

注意:

你可以假设两个字符串均只含有小写字母。

canConstruct(“a”, “b”) -> false
canConstruct(“aa”, “ab”) -> false
canConstruct(“aa”, “aab”) -> true

思路:

  • 只需要判断magazine是否可以组成ransom, 即maga中是否含有ran的所有字母, 最终数组中的每个 值不小于0即可

代码:

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int[] letter = new int[26];
        // 1. 判断长度是否合格
        if(ransomNote.length() > magazine.length()) return false;

        // 2. 统计magazine的字母
        for(int i = 0;i < magazine.length();i++) {
            letter[magazine.charAt(i) - 'a']++;
        }
        // 3. 统计ransomNote的字母
        for(int i = 0;i < ransomNote.length();i++) {
            letter[ransomNote.charAt(i) - 'a']--;
        }

        // 4. 判断letter是否合格
        for(int i = 0;i < letter.length;i++) {
            if(letter[i] < 0) return false;
        }

        return true;
    }
}
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

49.字母异位词分组

49. 字母异位词分组

思路:

  • 将每个字符串重新从大到小排列, 排序后相同的为一组收集到map中, 返回values

代码:

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        // 1. 建立容器
        Map<String,List<String>> m = new HashMap<>();

        // 2. 遍历, 排序
        for(String str : strs) {
            char[] s = str.toCharArray();
            Arrays.sort(s);

            // 3. 收集
            m.computeIfAbsent(new String(s),k -> new ArrayList<String>()).add(str);
        }

        // 4. 返回
        return new ArrayList<>(m.values());
    }
}

438.找到字符串中所有字母异位词

438. 找到字符串中所有字母异位词

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

法一:

  • 思路: 定长滑窗
  • 代码:
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        // 1. 定义容器, ans, 统计s, 统计p
        List<Integer> ans = new ArrayList<>();
        int[] cntS = new int[26];
        int[] cntP = new int[26];

        // 2. 统计p
        for(int i = 0;i < p.length();i++) {
            cntP[p.charAt(i) - 'a']++;
        }

        // 3. 统计s
        for(int r = 0;r < s.length();r++) {
            // 3.1 移入右端点
            cntS[s.charAt(r) - 'a']++;
            int l = r - p.length() + 1;
            if(l < 0) continue;

            // 3.2 更新
            if(Arrays.equals(cntP,cntS)) {
                ans.add(l);
            }

            // 3.3 移除左端点
            cntS[s.charAt(l) - 'a']--;
        }

        // 4. 返回
        return ans;
    }
}
- 时间复杂度:O(∣Σ∣_m_+_n_),其中 _m_ 是 _s_ 的长度,_n_ 是 _p_ 的长度,∣Σ∣=26 是字符集合的大小。
- 空间复杂度:O(∣Σ∣)。返回值不计入。

法二:

  • 思路: 不定长滑窗, 滑动时每次都保持s中r位置的字母数 <= p中该字母的数量, 如果某次循环中窗口中的长度等于p的长度, 则该窗口内为异位词, 记录l
  • 代码
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        // 1. 定义容器
        List<Integer> ans = new ArrayList<>();
        int[] cntP = new int[26];
        for(int i = 0;i < p.length();i++) cntP[p.charAt(i) - 'a']++;

        // 2. 处理s
        int l = 0;
        for(int r = 0;r < s.length();r++) {
            // 2.1 进右
            cntP[s.charAt(r) - 'a']--;

            // 2.2 判断该字母是否多进, 若是, 从左侧出
            while(cntP[s.charAt(r) - 'a'] < 0) {
                cntP[s.charAt(l++) - 'a']++;
            }

            // 2.3 判断循环是否找到字母异位词
            if(r - l + 1 == p.length()) ans.add(l);
        }

        // 3. 返回
        return ans;
    }
}

349.两个数组的交集

349. 两个数组的交集

法一:

  • 思路: set做hash表
  • 代码:
class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {

        // 1. 定义容器: 统计nums1的set, 记录ans的set
        Set<Integer> set = new HashSet<>();
        Set<Integer> equ = new HashSet<>();
        
        // 2. 统计nums1
        for(int n : nums1) {
            set.add(n);
        }

        // 3. 统计nums2, 记录equ
        for(int n : nums2) {
            if(set.contains(n)) {
                equ.add(n);
            }
        }

        // 4. 将equ转为数组, 返回
        int[] ans = new int[equ.size()];
        int i = 0;
        for(int n : equ) {
            ans[i++] = n;
        }
        return ans;

    }
}

法二:

  • 思路: 数组做hash表
  • 代码:
class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        // 1. 定义容器, 处理nums1的arr1, 处理nums2的arr2, 收集交集的set
        int[] arr1 = new int[1001];
        int[] arr2 = new int[1001];
        Set<Integer> equ = new HashSet<>();

        // 2. 处理nums1, nums2
        for(int i : nums1) {
            arr1[i]++;
        }

        for(int i : nums2) {
            arr2[i]++;
        }

        // 3. 收集交集
        for(int i = 0; i < 1001;i++) {
            if(arr1[i] > 0 && arr2[i] > 0) {
                equ.add(i);
            }
        }

        // 4. 返回
        int[] ans = new int[equ.size()];
        int i = 0;
        for(int n : equ) {
            ans[i++] = n;
        }
        return ans;
    }
}

350.两个数组的交集 II

350. 两个数组的交集 II

法一:

  • 思路: 数组哈希表
  • 代码:
class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        // 1. 统计nums1的arr1,统计nums2的arr2,记录答案的ans
        int[] arr1 = new int[1001];
        int[] arr2 = new int[1001];
        int[] ans = new int[1001];

        // 2. 遍历nums1, nums2
        for(int i : nums1) {
            arr1[i]++;
        }

        for(int i : nums2) {
            arr2[i]++;
        }

        // 3. 比较, 记录ans
        int j = 0;
        for(int i = 0;i < 1001;i++) {
            while(arr1[i] > 0 && arr2[i] > 0) {
                arr1[i]--;
                arr2[i]--;
                ans[j++] = i;
            }
        }

        // 4. 返回
        return Arrays.copyOfRange(ans,0,j);// [0,j)
    }
}
  • 时间复杂度:O(m+n)
  • 空间复杂度:O(m+n)

法二:

  • 思路: 排序 + 双指针, 找到两数组中相同的数, 收集
  • 代码
class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        int length1 = nums1.length, length2 = nums2.length;
        int[] intersection = new int[Math.min(length1, length2)];
        int index1 = 0, index2 = 0, index = 0;
        while (index1 < length1 && index2 < length2) {
            if (nums1[index1] < nums2[index2]) {
                index1++;
            } else if (nums1[index1] > nums2[index2]) {
                index2++;
            } else {
                intersection[index] = nums1[index1];
                index1++;
                index2++;
                index++;
            }
        }
        return Arrays.copyOfRange(intersection, 0, index);
    }
}
- 时间复杂度:O(mlogm+nlogn),其中 m 和 n 分别是两个数组的长度。对两个数组进行排序的时间复杂度是 O(mlogm+nlogn),遍历两个数组的时间复杂度是 O(m+n),因此总时间复杂度是 O(mlogm+nlogn)。
- 空间复杂度:O(min(m,n)),其中 m 和 n 分别是两个数组的长度。为返回值创建一个数组 equ,其长度为较短的数组的长度。

202.快乐数

202. 快乐数

**法一: **

  • 思路: set哈希表, 用set记录每次的平方和后的值, 判断是否重复, 若重复则不是快乐数, 否则是
  • 代码:
class Solution {
    // 计算每位平方和的函数
    public static int compute(int n) {
        int res = 0;
        while (n > 0) {
            int i = n % 10;
            n = n / 10;
            res += i * i;
        }
        return res;
    }

    public boolean isHappy(int n) {
        // 1. 容器, 收集每次计算的结果
        Set<Integer> set = new HashSet<>();
    
        // 2. 循环判断该数是不是快乐数
        while(n != 1 && !set.contains(n)) {
            set.add(n);
            n = compute(n);
        }

        // 3. 返回
        return n == 1;
    }
}

1.两数之和

1. 两数之和

思路: map哈希表, key为nums中的数, value为数的下标 (找Key的速度快)

**代码: **

class Solution {
    public int[] twoSum(int[] nums, int target) {
        // 1. 建立容器, 遍历nums的map
        Map<Integer,Integer> map = new HashMap<>();

        // 2. 遍历nums, 收集map
        for(int i = 0;i < nums.length;i++) {
            // 2.1 判断该位置的补数是否在map中
            // 2.1.1 在, 返回
            if(map.containsKey(target - nums[i])) {
                return new int[] {map.get(target - nums[i]), i};
            }

            // 2.2.2 不在, 本次的数值加入map
            map.put(nums[i],i);
        }

        // 3. 返回
        return null;
    }
}

454.四数相加 II

454. 四数相加 II

**思路: ** map哈希表, 四个数组两两组合, 变为两个数组的判断, 思路同1

**代码: **

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        // 1. 定义容器, 收集1+2的map, 返回的答案ans
        int ans = 0;
        Map<Integer,Integer> map = new HashMap<>();

        // 2. 计算1 2
        for(int i : nums1) {
            for(int j : nums2) {
                map.put(i + j,map.getOrDefault(i + j,0) + 1);
            }
        }

        // 3. 遍历3 4, 找补
        for(int i : nums3) {
            for(int j : nums4) {
                if(map.containsKey(0 - i - j)) {
                    ans += map.get(0 - i - j);
                }
            }
        }

        // 4. 返回
        return ans;
    }
}

15.三数之和

15. 三数之和

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

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

示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]

**思路: ** 双指针, 数组有序后, left指向i + 1, right指向nums.length - 1

代码:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        // 1. 定义容器, ans
        List<List<Integer>> ans = new ArrayList<>();

        // 2. 对原数组进行排序
        Arrays.sort(nums);

        // 3. 遍历查找
        for (int i = 0; i < nums.length; i++) {
            int left = i + 1;
            int right = nums.length - 1;
            // 3.1 对最外层进行剪枝,去重
            if (nums[i] > 0) // 剪枝
                return ans;// 从此之后不能再组成三元组
            if (i > 0 && nums[i] == nums[i - 1])
                continue;// 去重nums[i]

            // 3.2 移动left right
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];// 每次循环更新sum
                if (sum < 0) {
                    left++;
                } else if (sum > 0) {
                    right--;
                } else {
                    ans.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    // 找到一个三元组后, 对nums[left],nums[right] 去重
                    while (left < right && nums[left] == nums[left + 1])
                        left++;
                    while (left < right && nums[right] == nums[right - 1])
                        right--;
                    left++;
                    right--;
                }
            }

        }

        // 4. 返回
        return ans;
    }
}
  • 时间复杂度: O(n^2)
  • 空间复杂度: O(1)

18.四数之和

18. 四数之和

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

  • 0 <= a, b, c, d < n
  • abcd 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

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

**思路: ** 三层循环, 第一层控制i, 第二层控制j, 第三层控制left right

**代码: **

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        // 1. 定义容器, 返回ans
        List<List<Integer>> ans = new ArrayList<>();

        // 2. 对原数组排序
        Arrays.sort(nums);

        // 3. 一级剪枝, 去重
        for(int i = 0;i < nums.length;i++) {
            // 3.1 剪枝
            if(nums[i] > target && nums[i] >= 0) return ans;
            // 3.2 去重
            if(i > 0 && nums[i] == nums[i - 1]) continue;

            // 4. 二级剪枝, 去重
            for(int j = i + 1;j < nums.length;j++) {
                // 4.1 剪枝
                if(nums[i] + nums[j] > target && nums[i] + nums[j] >= 0) break;
                // 不能return, 因为此时可能nums[j]走到了一个较大值, 但是i++后可能有新的解
                // nums[i] + nums[j] > target不代表nums[++i] + nums[?](? < j) > target
                // 4.2 去重
                if(j > i + 1 && nums[j] == nums[j - 1]) continue; 

                // 5. 移动left right
                int left = j + 1;
                int right = nums.length - 1;

                while(left < right) {
                    long sum = (long)nums[i] + nums[j] + nums[left] + nums[right];

                    if(sum < target) {
                        left++;
                    }else if(sum > target) {
                        right--;
                    }else {
                        ans.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
                        // 5.1 对l f去重
                        while(left < right && nums[left] == nums[left + 1]) left++;
                        while(left < right && nums[right] == nums[right - 1]) right--;

                        left++;
                        right--;
                    }
                }
            }

        }

        return ans;
    }
}
  • 时间复杂度: O(n^3)
  • 空间复杂度: O(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值