哈希表理论基础
一般哈希表都是用来快速判断一个元素是否出现集合里
使用哈希表的话, 只需要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解法不可行