算法训练Day05 哈希表part01

哈希表理论基础 

一般哈希表都是用来快速判断一个元素是否出现集合里

使用哈希表的话, 只需要O(1)就可以做到

哈希法来解决问题,一般会选择如下三种数据结构。

  • 数组
  • set (集合)
  • map(映射)

就是用空间来换取时间的方法

疑问

遇到哈希问题我直接都用set不就得了,用什么数组啊。

直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的

242.有效的字母异位词

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

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

示例 1:

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

示例 2:

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

数组

26个元素来记录s中字符出现几次,t中出现的字符则对应的元素减1,最后检查数组元素是否为0

字符a到字符z的ASCII是26个连续的数值,利用连续的数值加工后作为数组的索引

class Solution {
    public boolean isAnagram(String s, String t) {
        int[] record = new int[26];

        for (int i = 0; i < s.length(); i++) {
            record[s.charAt(i) - 'a']++;     // 不需要记住字符a的ASCII,只要求出一个相对数值就可以了
        }

        for (int i = 0; i < t.length(); i++) {
            record[t.charAt(i) - 'a']--;
        }
        
      for(int i = 0;i < 26;i++){
          if (record[i] != 0){
              return false;
          }
      }
        
        return true;                        // record数组所有元素都为零0,说明字符串s和t是字母异位词
    }
}

暴力解法

找到对应的后将找到的字符标记为已占用

难点:found标识符如何设置,每层循环初始化found为false

public boolean isAnagram(String s, String t) {
    if (s.length() != t.length()) {
        return false;
    }
    
    // 将字符串转换为字符数组
    char[] sChars = s.toCharArray();
    char[] tChars = t.toCharArray();
    
    // 对每个字符进行比较
    for (int i = 0; i < sChars.length; i++) {
        boolean found = false;
        for (int j = 0; j < tChars.length; j++) {
            if (sChars[i] == tChars[j]) {
                // 在 t 中找到与 s[i] 相等的字符
                found = true;
                tChars[j] = '\0';  // 将找到的字符标记为已使用
                break;
            }
        }
        if (!found) {
            // 在 t 中没有找到与 s[i] 相等的字符
            return false;
        }
    }
    
    return true;
}

Arrays.sort

Arrays.sort 方法对字符数组进行排序,然后使用 Arrays.equals 方法比较排序后的字符数组是否相等

public boolean isAnagram(String s, String t) {
    if (s.length() != t.length()) {
        return false;
    }
    
    // 将字符串转换为字符数组
    char[] sChars = s.toCharArray();
    char[] tChars = t.toCharArray();
    
    // 对字符数组进行排序
    Arrays.sort(sChars);
    Arrays.sort(tChars);
    
    // 比较排序后的字符数组是否相等
    return Arrays.equals(sChars, tChars);
}

349. 两个数组的交集

给定两个数组 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] 也是可通过的
  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 1000

HashSet解法

题目要求给出唯一(去重)的且可无序的元素的,根据这两个特点选择unordered_set

import java.util.HashSet;
import java.util.Set;

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {
            return new int[0];
        }
        Set<Integer> set1 = new HashSet<>();
        Set<Integer> resSet = new HashSet<>();
        //遍历数组1
        for (int i : nums1) {
            set1.add(i);
        }
        //遍历数组2的过程中判断哈希表中是否存在该元素
        for (int i : nums2) {
            if (set1.contains(i)) {
                resSet.add(i);
            }
        }
      
        
        //方法1:另外申请一个数组存放setRes中的元素,最后返回数组
        int[] arr = new int[resSet.size()];
        int j = 0;
        for(int i : resSet){
            arr[j++] = i;
        }
        
        return arr;
    }
}
        //方法2:将结果集合转为数组

        //return resSet.stream().mapToInt(x -> x).toArray();

题目也给出了数值的范围因此可以用数组的方法

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        int[] hash1 = new int[1002];
        int[] hash2 = new int[1002];
        for(int i : nums1)
            hash1[i]++;
        for(int i : nums2)
            hash2[i]++;
        List<Integer> resList = new ArrayList<>();
        for(int i = 0; i < 1002; i++)
            if(hash1[i] > 0 && hash2[i] > 0)
                resList.add(i);
        int index = 0;
        int res[] = new int[resList.size()];
        for(int i : resList)
            res[index++] = i;
        return res;
    }
}

202.快乐数

编写一个算法来判断一个数 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

HashSet解法

题中表明了该题有两种结果--①结果为1  ②无限循环但不为1(也就是说有重复数字就是无限循环

利用这两点跳出循环

class Solution {
    public boolean isHappy(int n) {
        Set<Integer> record = new HashSet<>();
        while(n != 1 && !record.contains(n)){
            record.add(n);   
            n = process(n);
        }
        return n == 1;
    }
    private int process(int i){
        int tem = 0;
        int result = 0;
        while (i != 0){
            tem = i % 10;
            result += tem * tem;
            i = i / 10;
        }
        return result;
    }
}

383.赎金信

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

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

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

示例 1:

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

示例 2:

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

数组解法

此题与242.有效字母异位词同思路,也是只有小写字母

用二十六大小的数组来记录字符的次数,记录magazine的字符次数,

再用ransomNote来减去字符次数,最后判断每个数组元素是否小于0

如果小于零说明ransomNote不能由magazine组成,不符合题目要求

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int[] record = new int [26];  //用二十六大小的数组来记录字符出现的次数

        for (int i =0 ; i < magazine.length() ; i++){ //记录mazine出现的字符次数
            record[magazine.charAt(i)-'a']++;
        }

        for (int i =0 ; i < ransomNote.length() ; i++){ //减去记录的次数
            record[ransomNote.charAt(i)-'a']--;
        }
     
        for (int i =0 ; i < 26 ; i++){  //判断第一个字符串ransom能不能由
            if (record[i]<0){           //第二个字符串magazines里面的字符构成
                return false;
            }
        }
        return true;
    }
}

也可用242.暴力解法,但Arrays.sort解法不可行

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值