哈希表
数据结构简介…
高频面试题
242.有效的字母异位词
🚀题目链接:LeetCode242.有效的字母异位词
题目:
给定两个字符串s
和t
,编写一个函数来判断t
是否是s
的字母异位词。
注意: 若s
和t
中每个字符出现的次数都相同,则称s
和t
互为字母异位词。示例 1:
输入: s = "anagram", t = "nagaram" 输出: true
示例 2:
输入: s = "rat", t = "car" 输出: false
🍬C++ AC代码:
class Solution {
public:
bool isAnagram(string s, string t) {
if (s.size() != t.size())
return false;
map<char, int> char_map;
for (int i = 0; i < s.size(); i++)
char_map[s[i]]++;
for (int i = 0; i < t.size(); i++) {
if (char_map[t[i]] == 0)
return false;
char_map[t[i]]--;
}
return true;
}
};
✨Tips:
-
⭐
map
的key
为单词中的字母,value
为字母出现的次数。 -
⭐第一次遍历统计字符串
s
中每个字母出现的次数,第二次遍历字符串t
,每出现一个字母就在原来的统计量上 − 1 -1 −1,同时C++的map有一个特点,当map中访问的key
不存在时会自动生成一个键值对,默认值为0,因此每次可以先判断字母的统计量是否为0再减一来检查异位词。 -
⭐这种方法排除了第二个字符串的某个字母在第一个字符串中没有出现的可能,同时也排除了第二个字符的某个字母数量比第一字符串多的可能。
-
⭐当然还有一种情况,就是第一个字符串的某个字母数量比第二个字符串多的情况,但如果两个字符串长度相同,某个字符数量多,必然意味着其他字符的数量第一个字符串会比第二个字符串少,这种情况在上面已经考虑进去了,只需要在最开始判断一下两个字符串长度,如果长度不相等直接返回
false
。 -
⭐本题最多有26个不同的字母,我们也可以直接使用一个数组来充当
map
,数组下标为字母的ASCII码值,通常把用数组冲当map
的方式叫做散列,是一种很常用的技巧:class Solution { public: bool isAnagram(string s, string t) { int map[26] = {0}; for (int i = 0; i < s.size(); i++) map[s[i] - 'a']++; for (int i = 0; i < t.size(); i++) map[t[i] - 'a']--; for (int i = 0; i < 26; i++) if (map[i] != 0) return false; return true; } };
- ⭐
s[i] - 'a'
两个字母直接相减,实际上是ASCII码值相减,计算出字母相较于a
的偏移量,将码值映射到0~26的范围中。
- ⭐
☕Java AC代码:
class Solution {
public boolean isAnagram(String s, String t) {
if (s.length() != t.length())
return false;
Map<Character, Integer> charMap = new HashMap<>();
for (int i = 0; i < s.length(); i++)
charMap.put(s.charAt(i), charMap.getOrDefault(s.charAt(i), 0) + 1);
for (int i =0; i < t.length(); i++) {
if (charMap.getOrDefault(t.charAt(i), 0) == 0)
return false;
charMap.replace(t.charAt(i), charMap.get(t.charAt(i)) - 1);
}
return true;
}
}
✨Tips:
-
⭐Java的
map
使用起来没有C++那么方便,而且用在本题中算法的效率也不是很高,建议直接使用一个数组来代替map
:class Solution { public boolean isAnagram(String s, String t) { int[] map = new int[26]; for (int i = 0; i < s.length(); i++) map[s.charAt(i) - 'a']++; for (int i = 0; i < t.length(); i++) map[t.charAt(i) - 'a']--; for (int i = 0; i < 26; i++) if (map[i] != 0) return false; return true; } }
🍦Python AC代码:
class Solution(object):
def isAnagram(self, s, t):
dic1, dic2 = {}, {}
for ch in s:
dic1[ch] = dic1.get(ch, 0) + 1
for ch in t:
dic2[ch] = dic2.get(ch, 0) + 1
return dic1 == dic2
✨Tips:
- ⭐Python中使用
{}
表示一个字典(dict),相当于map
,并且可以直接用==
判断两个字典是否相同。
2.两数之和
🚀题目链接:LeetCode1.两数之和
题目:
给定一个整数数组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]
🍬C++ AC代码:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hash_table;
for (int i = 0; i < nums.size(); i++) {
if (hash_table.find(target - nums[i]) != hash_table.end())
return {hash_table[target - nums[i]], i};
hash_table[nums[i]] = i;
}
return {};
}
};
✨Tips:
- ⭐
map.find()
用于查找某个key
是否在map
中,如果存在则返回对应位置的迭代器,如果不存在,返回迭代器map.end()
。 - ⭐迭代器可以简单理解为用来遍历集合的工具,迭代器的迭代的对象是集合里面存放对象的引用的拷贝。
- ⭐这里用
nums[i]
作为key
,下标i
作为value
,是因为map
通过计算哈希(Hash)值来查找key
,时间复杂度 O ( 1 ) O(1) O(1),而查找value
的复杂度就不是常数级别了。
☕Java AC代码:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hashTable = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (hashTable.get(target - nums[i]) != null)
return new int[]{hashTable.get(target - nums[i]), i};
hashTable.put(nums[i], i);
}
return null;
}
}
✨Tips:
- ⭐Java中的
map.get(key)
方法用来返回对应的value
,如果key
不存在,则返回null
。
🍦Python AC代码:
class Solution(object):
def twoSum(self, nums, target):
dic = {}
for i in range(len(nums)):
if dic.get(target - nums[i]) is not None:
return [dic[target - nums[i]], i];
dic[nums[i]] = i;
return None
✨Tips:
- ⭐Python中
dict.get(key)
方法用于返回字典中key
对应的value
,如果key
不存在,则返回None
。
3.三数之和
🚀题目链接:LeetCode15.三数之和
题目:
给你一个整数数组nums
,判断是否存在三元组[nums[i], nums[j], nums[k]]
满足i != j
、i != k
且j != 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 。
🍬C++ AC代码:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int> > res;
set<vector<int> > temp_res;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++) {
if (i != 0 && nums[i] == nums[i - 1])
continue;
unordered_set<int> hash_set;
for (int j = i + 1; j < nums.size(); j++) {
if (hash_set.find(nums[j]) == hash_set.end())
hash_set.insert(-nums[i] - nums[j]);
else
temp_res.insert({nums[i], -nums[i] - nums[j], nums[j]});
}
}
res.assign(temp_res.begin(), temp_res.end());
return res;
}
};
✨Tips:
- ⭐“两数之和”利用
map
可以让时间复杂度从 O ( n 2 ) O(n^2) O(n2)减小到 O ( n ) O(n) O(n),类似的,“三数之和”我们可以枚举其中两个值,第三个值用map
进行优化。 - ⭐与"两数之和"不同的是本题我们不需要返回下标,因此我们可以使用
set
来代替map
,set
的底层也是借助map
来实现的,不过它只能存key
,不能存value
,存入的值会自动去重。 - ⭐为了避免找到重复的三元组,在一开始先对
nums
进行了一个排序,并且将满足条件的三元组先存入set
,借助三元组有序这一特点取重,最后再将set
转换成vector
返回。 - ⭐
res.assign(temp_res.begin(), temp_res.end())
将set
转换成vector
。
☕Java AC代码:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Set<ArrayList<Integer>> res = new HashSet<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (i != 0 && nums[i] == nums[i - 1])
continue;
Set<Integer> set = new HashSet<>();
for (int j = i + 1; j < nums.length; j++) {
if (!set.contains(nums[j]))
set.add(-nums[i] - nums[j]);
else {
ArrayList<Integer> tempList = new ArrayList<>();
tempList.add(nums[i]);
tempList.add(-nums[i] - nums[j]);
tempList.add(nums[j]);
res.add(tempList);
}
}
}
return new ArrayList<List<Integer>>(res);
}
}
✨Tips:
- ⭐与C++的思路相同,Java中
set
转list
只需要在list
构造方法中传入set
:new ArrayList<List<Integer>>(res)
。
🍦Python AC代码:
class Solution(object):
def threeSum(self, nums):
nums.sort()
res = set()
for i, v in enumerate(nums[:-2]):
if i >= 1 and v == nums[i - 1]:
continue
dic = set()
for x in nums[i+1:]:
if x not in dic:
dic.add(-v-x)
else:
res.add((v, -v-x, x))
return map(list, res)
✨Tips:
- ⭐
for i, v in enumerate(nums[:-2])
表示同时遍历下标与数组值,[:-2]
表示遍历到数组的倒数第二个位置。 - ⭐
for x in nums[i+1:]
表示从数组的第i + 1
个值开始遍历。 - ⭐Python中用
map(list, res)
将set
类型的res
转换成list
类型。