哈希表
比较数组/字符串
242. 有效的字母异位词
给定两个字符串s
和t
,编写一个函数来判断t
是否是s
的字母异位词。
注意:若s
和t
中每个字符出现的次数都相同,则称s
和t
互为字母异位词。
示例1:
1
2
输入: s = "anagram", t = "nagaram"
输出: true
示例2:
1
2
输入: s = "rat", t = "car"
输出: false
提示:
- 1 <=
s.length, t.length
<= 5 * 10⁴ -
s
和t
仅包含小写字母
Solution
本题的解法在于使用哈希表来统计s
和t
中字符出现的个数。如果两个哈希表完全相等则返回True
,否则返回False
。
题目链接:
python代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
if len(s) != len(t):
return False
s_dict = {}
t_dict = {}
for char in s:
s_dict[char] = s_dict.get(char, 0) + 1
for char in t:
t_dict[char] = t_dict.get(char, 0) + 1
return s_dict == t_dict
C++代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
bool isAnagram(string s, string t) {
if (s.length() != t.length()) return false;
int table[26] = {0};
for (auto& ch : s) {
++table[ch - 'a'];
}
for (auto& ch : t) {
--table[ch - 'a'];
if (table[ch - 'a'] < 0) return false;
}
return true;
}
};
当然本题也可以使用python内置的Counter类来进行处理。很多记数问题直接使用Counter
类会更容易一些。
1
2
3
4
5
6
7
8
class Solution(object):
def isAnagram(self, s: str, t: str) -> bool:
from collections import Counter
a_count = Counter(s)
b_count = Counter(t)
return a_count == b_count
205. 同构字符串
给定两个字符串s
和t
,判断它们是否是同构的。
如果s
中的字符可以按某种映射关系替换得到t
,那么这两个字符串是同构的。
每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。
示例1:
1
2
输入:s = "egg", t = "add"
输出:true
示例2:
1
2
输入:s = "foo", t = "bar"
输出:false
示例3:
1
2
输入:s = "paper", t = "title"
输出:true
提示:
- 1 <=
s.length
<= 5 * 10⁴ -
t.length
==s.length
-
s
和t
由任意有效的ASCII字符组成
Solution
本题的解法在于使用两个哈希表s2t
和t2s
分别表示s
到t
和t
到s
之间的映射。如果这两个映射都是一一映射(双射)则返回True
,否则返回False
。
题目链接:
python代码:
1
2
3
4
5
6
7
8
9
10
11
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
s2t, t2s = {}, {}
for ss, tt in zip(s, t):
if (ss in s2t and s2t[ss] != tt) or \
(tt in t2s and t2s[tt] != ss):
return False
s2t[ss], t2s[tt] = tt, ss
return True
C++代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
bool isIsomorphic(string s, string t) {
unordered_map<char, char> s2t;
unordered_map<char, char> t2s;
for (int i=0; i < s.length(); ++i) {
char ss = s[i];
char tt = t[i];
if ((s2t.count(ss) && s2t[ss] != tt) || (t2s.count(tt) && t2s[tt] != ss)) {
return false;
}
s2t[ss] = tt;
t2s[tt] = ss;
}
return true;
}
};
寻找指定元素
202. 快乐数
编写一个算法来判断一个数n
是不是快乐数。
「快乐数」定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为
1
,也可能是无限循环但始终变不到1
。 - 如果这个过程结果为
1
,那么这个数就是快乐数。
如果n
是「快乐数」就返回true
;不是,则返回false
。
示例1:
1
2
3
4
5
6
7
输入:n = 19
输出:true
解释:
1² + 9² = 82
8² + 2² = 68
6² + 8² = 100
1² + 0² + 0² = 1
示例2:
1
2
输入:n = 2
输出:false
提示:
- 1 <=
n
<= 2³¹ - 1
Solution
本题的难点在于如何考虑无限循环的问题。为了方便判断我们需要使用一个集合nums
来存储已经收集到的结果,如果数字n
出现在nums
中则说明发生了循环直接返回False
;否则按照题目公式生成下一个数字直到它等于1
并返回True
。
题目链接:
python代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution:
def isHappy(self, n: int) -> bool:
def nextHappy(n: int) -> int:
res = 0
while n:
res += (n % 10) * (n % 10)
n = n // 10
return res
nums = set()
while n != 1:
if n in nums:
return False
nums.add(n)
n = nextHappy(n)
return True
C++代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public:
bool isHappy(int n) {
unordered_set<int> nums;
while (nums.count(n) == 0) {
nums.emplace(n);
if (n == 1) return true;
n = nextHappy(n);
}
return false;
}
int nextHappy(int n) {
int res = 0;
while (n) {
int x = n % 10;
res += x*x;
n = n / 10;
}
return res;
}
};
1. 两数之和
给定一个整数数组nums
和一个整数目标值target
,请你在该数组中找出和为目标值target
的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
1
2
3
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
1
2
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
1
2
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
- 2 <=
nums.length
<= 10⁴ - -10⁹ <=
nums[i]
<= 10⁹ - -10⁹ <=
target
<= 10⁹ - 只会存在一个有效答案
Solution
两数之和是哈希表的经典问题。本题中我们需要使用一个哈希表records
对nums
进行遍历,records
的键值分别为nums[idx]
和idx
。这样在遍历时如果发现target-nums[idx]
在records
中则说明找到了和为target
的一对数字,返回[idx, records[target-nums[idx]]]
即可;否则继续遍历直至结束并返回[]
。
题目链接:
python代码:
1
2
3
4
5
6
7
8
9
10
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
records = {}
for idx, num in enumerate(nums):
if target-num in records:
return [idx, records[target-num]]
records[num] = idx
return []
C++代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> table;
for (int i=0; i < nums.size(); ++i) {
int x = nums[i];
if (table.count(target - x)) {
return vector<int> {i, table[target - x]};
} else {
table[x] = i;
}
}
return {};
}
};
454. 四数相加 II
给你四个整数数组nums1
、nums2
、nums3
和nums4
,数组长度都是n
,请你计算有多少个元组(i, j, k, l)
能满足:
- 0 <=
i, j, k, l
<n
-
nums1[i] + nums2[j] + nums3[k] + nums4[l]
== 0
示例1:
1
2
3
4
5
6
输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
示例2:
1
2
输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
输出:1
提示:
-
n
==nums1.length
-
n
==nums2.length
-
n
==nums3.length
-
n
==nums4.length
- 1 <=
n
<= 200 - -2²⁸ <=
nums1[i], nums2[i], nums3[i], nums4[i]
<= 2²⁸
Solution
本题中我们可以把四个数组分为nums1
和nums2
以及nums3
和nums4
两组。首先对nums1
和nums2
进行遍历,使用一个哈希表ret
记录所有可能的数字之和nums1[i]+nums2[j]
及其出现的次数。然后对nums3
和nums4
进行遍历,如果-(nums3[k]+nums4[l])
出现在ret
中则说明出现了满足和为0的元组,令count += ret[-(nums3[k]+nums4[l])]
,最后返回res
即可。
题目链接:
python代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution:
def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
ret = {}
for a in nums1:
for b in nums2:
ret[a+b] = ret.get(a+b, 0) + 1
count = 0
for c in nums3:
for d in nums4:
if -(c+d) in ret:
count += ret[-(c+d)]
return count
C++代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> ret;
for (int x : nums1) {
for (int y: nums2) {
++ret[x+y];
}
}
int count = 0;
for (int x : nums3) {
for (int y: nums4) {
if (ret.count(-x-y)) {
count += ret[-x-y];
}
}
}
return count;
}
};
公共元素
1002. 查找共用字符
给你一个字符串数组words
,请你找出所有在words
的每个字符串中都出现的共用字符(包括重复字符),并以数组形式返回。你可以按任意顺序返回答案。
示例1:
1
2
输入:words = ["bella","label","roller"]
输出:["e","l","l"]
示例2:
1
2
输入:words = ["cool","lock","cook"]
输出:["c","o"]
提示:
- 1 <=
words.length
<= 100 - 1 <=
words[i].length
<= 100 -
words[i]
由小写英文字母组成
Solution
本题的解法在于使用哈希表来统计words
中每个单词里每个字母出现的频率,然后对每个字母的频率取最小值来得到公共字符。得到字符后将它们按频率输出到res
中即可。
题目链接:
python代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution:
def commonChars(self, words: List[str]) -> List[str]:
minFreq = [float("inf") for i in range(26)]
for word in words:
freq = [0 for i in range(26)]
for char in word:
freq[ord(char) - ord('a')] += 1
for i in range(26):
minFreq[i] = min(minFreq[i], freq[i])
res = []
for i in range(26):
for _ in range(minFreq[i]):
res.append(chr(ord('a')+i))
return res
C++代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public:
vector<string> commonChars(vector<string>& words) {
int minFreq[26];
for (int i = 0; i < 26; ++i) {
minFreq[i] = INT_MAX;
}
for (auto &word : words) {
int freq[26] = {0};
for (auto &ch : word) {
++freq[ch - 'a'];
}
for (int i = 0; i < 26; ++i) {
minFreq[i] = min(minFreq[i], freq[i]);
}
}
vector<string> res;
for (int i = 0; i < 26; ++i) {
for (int j = 0; j < minFreq[i]; ++j) {
string s(1, 'a' + i); // convert 'a'+i to string
res.emplace_back(s);
}
}
return res;
}
};
本题同样可以使用python内置的Counter类来进行处理。使用字符串word
来初始化时会统计word
中字符出现的个数,然后需要使用Counter
类的交集运算符&
对每个单词进行遍历从而得到公共字符。最后按照公共字符的频率进行输出即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def commonChars(self, words: List[str]) -> List[str]:
from collections import Counter
freq = Counter(words[0])
for word in words:
freq = freq & Counter(word)
res = []
for k, v in freq.items():
for _ in range(v):
res.append(k)
return res
349. 两个数组的交集
给定两个数组nums1
和nums2
,返回它们的交集。输出结果中的每个元素一定是唯一的。我们可以不考虑输出结果的顺序。
示例1:
1
2
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例2:
1
2
3
输入: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
Solution
本题只需要将两个数组转换为集合并计算它们的交集即可。
题目链接:
python代码:
1
2
3
4
5
6
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
set1 = {num: 1 for num in nums1}
set2 = {num: 1 for num in nums2}
return [num for num in set1 if num in set2]
1
2
3
4
5
6
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
set1 = set(nums1)
set2 = set(nums2)
return list(set1 & set2)
C++代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> set1, set2;
for (auto &x : nums1) set1.emplace(x);
for (auto &x : nums2) set2.emplace(x);
return setIntersection(set1, set2);
}
vector<int> setIntersection(unordered_set<int>& set1, unordered_set<int>& set2) {
if (set1.size() > set2.size()) return setIntersection(set2, set1);
vector<int> res;
for (auto &x : set1) {
if (set2.count(x)) res.emplace_back(x);
}
return res;
}
};
383. 赎金信
给你两个字符串:ransomNote
和magazine
,判断ransomNote
能不能由magazine
里面的字符构成。
如果可以,返回true
;否则返回false
。
magazine
中的每个字符只能在ransomNote
中使用一次。
示例1:
1
2
输入:ransomNote = "a", magazine = "b"
输出:false
示例2:
1
2
输入:ransomNote = "aa", magazine = "ab"
输出:false
示例3:
1
2
输入:ransomNote = "aa", magazine = "aab"
输出:true
提示:
- 1 <=
ransomNote.length, magazine.length
<= 10⁵。 -
ransomNote
和magazine
由小写英文字母组成。
Solution
本题只需要分别统计ransomNote
和magazine
中字符出现的频率,然后判断ransomNote
中的频率是否都大于等于magazine
中字符的频率即可。
题目链接:
python代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
chars = {}
for char in magazine:
chars[char] = chars.get(char, 0) + 1
for char in ransomNote:
chars[char] = chars.get(char, 0) - 1
for char, count in chars.items():
if count < 0:
return False
return True
C++代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
if (ransomNote.size() > magazine.size()) return false;
int freq[26] = {0};
for (auto &ch : magazine) {
++freq[ch - 'a'];
}
for (auto &ch : ransomNote) {
--freq[ch - 'a'];
if (freq[ch - 'a'] < 0) return false;
}
return true;
}
};
除此之外本题同样可以使用python内置的Counter类来进行处理。这里需要使用到Counter
类的差集运算符-
,它会对两个Counter
对象具有相同键的值作差并只保留正数部分。因此我们只需要判断rCount - mCount
的长度是否为0
即可。
题目链接:
1
2
3
4
5
6
7
8
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
from collections import Counter
rCount = Counter(ransomNote)
mCount = Counter(magazine)
return len(rCount - mCount) == 0