LeetCode 算法哈希表

179 阅读10分钟

1.两数之和--easy

给定一个整数数组 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]

提示:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

我的题解:

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

思路:

  • 遍历数组,使用target减数组里的值,看看这个结果是否在哈希表中。
  • 不在,将这个值和key存入哈希表
  • 在,则找到了结果,将结果返回。
  • 在这里,key是数组的值,value是数组的下标。
  • 如果最终都没有结果则抛出异常

2.有效的字母异位词--easy

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

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

示例 1:

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

示例 2:

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

提示:

  • 1 <= s.length, t.length <= 5 * 104
  • st 仅包含小写字母

题解:

  class Solution {
      public boolean isAnagram(String s, String t) {
          int[] arr=new int[26];
          for(int i=0;i<s.length();i++){
              arr[s.charAt(i)-'a']+=1;
          }
          for(int i=0;i<t.length();i++){
              arr[t.charAt(i)-'a']-=1;
          }
          for(int i=0;i<26;i++){
              if(arr[i]!=0){
                  return false;
              }
          }
          return true;
      }
  }

思路:

哈希表--有效的字符串异位.jpg

3.赎金信--easy

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

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

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

示例 1:

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

示例 2:

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

示例 3:

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

提示:

  • 1 <= ransomNote.length, magazine.length <= 105
  • ransomNotemagazine 由小写英文字母组成

题解:

 class Solution {
     public boolean canConstruct(String ransomNote, String magazine) {
         int[] arr=new int[26];
         for(int i=0;i<magazine.length();i++){
             arr[magazine.charAt(i)-'a']+=1;
         }
         for(int i=0;i<ransomNote.length();i++){
             arr[ransomNote.charAt(i)-'a']-=1;
         }
         for(int i=0;i<26;i++){
             if(arr[i]<0){
                 return false;
             }
         }
         return true;
     }
 }

思路:

同有效的字母异位词

4.数组的交集--easy

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

示例 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) {
         int[] arr=new int[1001];
         Set<Integer> set = new HashSet<>();
         int a=0;
         for(int i=0;i< nums1.length;i++){
             arr[nums1[i]]=1;
         }
         for(int i=0;i< nums2.length;i++){
             if (arr[nums2[i]]==1) {
                 arr[nums2[i]]-=1;
                 set.add(nums2[i]);
             }
         }
         int[] arr2=new int[set.size()];
         int j=0;
         for(int i : set){
             arr2[j++] = i;
         }
         return arr2;
     }
 }

思路:

  • 还是利用桶排的思想,定义大小为1001的数组,如果有值,则将数组里对应的值变为一
  • 再循环遍历第二个数组,找到值为一,将其存到一个集合之中(防止重复),然后将值减一
  • 最后将集合转变为数组

5.快乐数--easy

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n快乐数 就返回 true ;不是,则返回 false

示例 1:

 输入:n = 19
 输出:true
 解释:
 12 + 92 = 82
 82 + 22 = 68
 62 + 82 = 100
 12 + 02 + 02 = 1

示例 2:

 输入:n = 2
 输出:false

提示:

  • 1 <= n <= 231 - 1

题解1-----使用哈希表:

 class Solution {
     public boolean isHappy(int n) {
         Set<Integer> set=new HashSet<>();
         //如果新求得的数在哈希表中,证明循环
         while (n!=1 && !set.contains(n)){
             set.add(n);
             n=getSum(n);
         }
         return n==1;
     }
     public int getSum(int n){
         int sum=0;
         while(n>0){
             sum+=(n%10)*(n%10);
             n=n/10;
         }
         return sum;
     }
 }

思路:

题上已知无限循环,则将每次求得的平方和存入哈希表中

  • 如果新求得的数在哈希表中,证明循环
  • 如果结果为1,则是快乐数

题解2----使用快慢指针:

 class Solution {
     public boolean isHappy(int n) {
         int fast=n,slow=n;
         do {
             slow=getSum(slow);
             fast=getSum(fast);
             fast=getSum(fast);
         }while (fast!=slow);
         return fast==1;
     }
     public int getSum(int n){
         int sum=0;
         while(n>0){
             sum+=(n%10)*(n%10);
             n=n/10;
         }
         return sum;
     }
 }

思路:

循环问题使用快慢指针一般可以解决,只要数据规模不是很大 在int中199999999的位数的平方和为最大为:1+81*9=720 所以快乐数的取值范围为[1,720]; 所以一定会出现循环, 又因为1的快乐数为1,本身就是一个循环

所以循环只有两种可能

  • 多个数循环
  • 1自身循环

6.有序数组的平方----easy

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例 2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 已按 非递减顺序 排序

题解1----暴力解法

 class Solution {
     public int[] sortedSquares(int[] nums) {
         int[] s=new int[nums.length];
         for(int i=0;i< nums.length;i++){
             s[i]=nums[i]*nums[i];
         }
         Arrays.sort(s);
         return s;
     }
 }

题解2----双指针(最优解)

 class Solution {
     public int[] sortedSquares(int[] nums) {
         int[] s=new int[nums.length];
         int l=0,r= nums.length-1,i= nums.length-1;
         while (l<=r){
             int re=nums[r]*nums[r];
             int le=nums[l]*nums[l];
             if(re>le){
                 s[i]=re;
                 i--;
                 r=r-1;
             }else{
                 s[i]=le;
                 i--;
                 l=l+1;
             }
         }
         return s;
     }
 }

思路:

  • 定义左右两个指针,向中间走
  • 比较其大小,将大的放在最大位置

7.三数之和----medium

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

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

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1][-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0

提示:

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

题解:

 class Solution {
     public List<List<Integer>> threeSum(int[] nums) {
         List<List<Integer>> res = new ArrayList<>();
         Arrays.sort(nums);
         // 找出a + b + c = 0
         // a = nums[i], b = nums[l], c = nums[r]
         int l=0,r=0;
         for(int i=0;i< nums.length;i++){
             if (nums[0] > 0) {
                 return res;
             }
             if (i>0&&nums[i]==nums[i-1]){//去重a
                 continue;
             }
             l=i+1;
             r=nums.length-1;
             while(l<r){
                 int sum=nums[i]+nums[l]+nums[r];
                 if(sum>0){
                     r--;
                 }else if (sum<0){
                     l++;
                 }else {
                     res.add(Arrays.asList(nums[i],nums[l],nums[r]));
                     // 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
                     while(r>l && nums[r]==nums[r-1]) r--;
                     while(r>l && nums[l]==nums[l+1]) l++;
                     r--;
                     l++;
                 }
             }
         }
         return res;
     }
 }

思路:

三数之和.jpg

8.四数之和----medium

给你一个由 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

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

示例 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]]

提示:

  • 1 <= nums.length <= 200
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109

题解:

 class Solution {
     public List<List<Integer>> fourSum(int[] nums, int target) {
         List<List<Integer>> res = new ArrayList<>();
         Arrays.sort(nums);
         // 找出a + b + c + d= 0
         // a = nums[i], b = nums[j],c = nums[l], d = nums[r]
         for(int i=0;i< nums.length;i++){
             //初始值大于,则没机会
             if (nums[i] > target&&nums[i] > 0) {
                 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 left = j + 1;
                 int right = nums.length - 1;
                 while (right > left) {
                     // nums[k] + nums[i] + nums[left] + nums[right] > target int会溢出
                     long sum = (long) 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]));
                         // 对nums[left]和nums[right]去重
                         while (right > left && nums[right] == nums[right - 1]) right--;
                         while (right > left && nums[left] == nums[left + 1]) left++;
 
                         left++;
                         right--;
                     }
                 }
             }
         }
         return res;
     }
 }

思路:

同三数之和

10.同构字符串----easy

给定两个字符串 st ,判断它们是否是同构的。

如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。

每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。

示例 1:

输入:s = "egg", t = "add"
输出:true

示例 2:

输入:s = "foo", t = "bar"
输出:false

示例 3:

输入:s = "paper", t = "title"
输出:true

提示:

  • 1 <= s.length <= 5 * 104
  • t.length == s.length
  • st 由任意有效的 ASCII 字符组成

题解:

  class Solution {
      public boolean isIsomorphic(String s, String t) {
          Map<Character, Character> map1 = new HashMap<>();
          Map<Character, Character> map2 = new HashMap<>();
          for (int i = 0, j = 0; i < s.length(); i++, j++) {
              if (!map1.containsKey(s.charAt(i))) {
                  map1.put(s.charAt(i), t.charAt(j)); // map1保存 s[i] 到 t[j]的映射
              }
              if (!map2.containsKey(t.charAt(j))) {
                  map2.put(t.charAt(j), s.charAt(i)); // map2保存 t[j] 到 s[i]的映射
              }
              // 无法映射,返回 false
              if (map1.get(s.charAt(i)) != t.charAt(j) || map2.get(t.charAt(j)) != s.charAt(i)) {
                  return false;
              }
          }
          return true;
      }
  }

思路:

同构字符串.jpg

11.查找公用字符----easy

给你一个字符串数组 words ,请你找出所有在 words 的每个字符串中都出现的共用字符( 包括重复字符),并以数组形式返回。你可以按 任意顺序 返回答案。

示例 1:

 输入:words = ["bella","label","roller"]
 输出:["e","l","l"]

示例 2:

 输入:words = ["cool","lock","cook"]
 输出:["c","o"]

提示:

  • 1 <= words.length <= 100
  • 1 <= words[i].length <= 100
  • words[i] 由小写英文字母组成

题解:

  class Solution {
      public List<String> commonChars(String[] A) {
          List<String> result = new ArrayList<>();
          if (A.length == 0) return result;
          int[] hash= new int[26]; // 用来统计所有字符串里字符出现的最小频率
          for (int i = 0; i < A[0].length(); i++) { // 用第一个字符串给hash初始化
              hash[A[0].charAt(i)- 'a']++;
          }
          // 统计除第一个字符串外字符的出现频率
          for (int i = 1; i < A.length; i++) {
              int[] hashOtherStr= new int[26];
              for (int j = 0; j < A[i].length(); j++) {
                  hashOtherStr[A[i].charAt(j)- 'a']++;
              }
              // 更新hash,保证hash里统计26个字符在所有字符串里出现的最小次数
              for (int k = 0; k < 26; k++) {
                  hash[k] = Math.min(hash[k], hashOtherStr[k]);
              }
          }
          // 将hash统计的字符次数,转成输出形式
          for (int i = 0; i < 26; i++) {
              while (hash[i] != 0) { // 注意这里是while,多个重复的字符
                  char c= (char) (i+'a');
                  result.add(String.valueOf(c));
                  hash[i]--;
              }
          }
          return result;
      }
  }

思路:

很简单,看题了就会了