题目:242 有效的字母异位词、349 两个数组的交集、202 快乐数、1 两数之和
打卡日期:20204.2.1、2.4
242 有效的字母异位词
数组就是简单的哈希表
当使用哈希表解决问题时,常使用以下三种数据结构:
- 数组
- set(集合)
- map(映射)
class Solution {
public:
bool isAnagram(string s, string t) {
int record[26] = {0};
bool flag = true;
for (int i=0; i<s.size(); i++) {
record[s[i] - 'a']++;
}
for (int i=0; i<t.size(); i++) {
record[t[i] - 'a']--;
}
for (int i=0; i<26; i++) {
if (record[i] != 0) {
flag = false;
break;
}
}
return flag;
}
};
时间复杂度:O(n)
空间复杂度:O(1)
349 两个数组的交集
数组
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set; // 存放结果
int hash[1000] = {0};
for (int num : nums1) {
hash[num] = 1;
}
for (int num : nums2) {
if (hash[num] == 1) {
result_set.insert(num);
}
}
vector<int> result(result_set.begin(), result_set.end());
return result;
}
};
时间复杂度: O(m + n),m是最后要把set转成vector
空间复杂度: O(n)
集合(set)
如果哈希值较少、特别分散、跨度较大,使用数组将造成空间的浪费
可以考虑另一种结构体:set 集合,集合里的元素保持唯一性(已去重):
- std::set
- std::multiset
- std::unordered_set
set 和 multiset 的底层实现都是红黑树,unordered_set 底层实现是哈希表,使用 unordered_set 效率是最高的,不需要对数据进行排序
———————————————————————————————————
那么遇到哈希问题可以都用set吗?
- No!
直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的。不要小瞧这个耗时,在数据量大的情况,差距是很明显的。
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set; //存放结果
unordered_set<int> nums_set(nums1.begin(), nums1.end());
// 检验nums2的数字是否在nums1的集合中出现过
for (int num : nums2) {
if (nums_set.find(num) != nums_set.end()) { // 若出现过
result_set.insert(num);
}
}
vector<int> result(result_set.begin(), result_set.end()); // 转化为vector结构返回
return result;
}
};
nums_set.find(num)
是调用集合(set)的find
方法,该方法返回一个指向包含元素 num 的位置的迭代器。如果 num 存在于集合中,迭代器指向该元素的位置;如果不存在,则返回集合的末尾(end)。
时间复杂度:O(n+m),m是最后要把set转成vector
空间复杂度:O(n)
202 快乐数
思路:
题目中说会无限循环,即在求和的过程中,sum会重复出现。正如我们所知,当我们遇到要快速判断一个元素是否出现在集合中时,就要考虑哈希法。
因此,这道题使用哈希法来判断sum是否重复出现(unordered_set),若是则return false,否则直至sum为1.
class Solution {
public:
int getSum(int n) {
int sum = 0;
while (n) {
sum += (n%10) * (n%10);
n = n / 10;
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> set;
bool flag = false;
while (1) {
int sum = getSum(n);
if (sum == 1) {
flag = true;
break;
}
if (set.find(sum) != set.end()) { // 在集合中,已经存在
break;
} else {
set.insert(sum);
}
n = sum;
}
return flag;
}
};
时间复杂度: O(logn)
空间复杂度: O(logn)
1 两数之和
注:题目要求是两个数,因此两下标不能一样
暴力解法
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
for (int i=0; i<nums.size(); i++) {
for (int j=i+1; j<nums.size(); j++) {
if (nums[i] + nums[j] == target) {
return {i, j};
}
}
}
return {};
}
};
时间复杂度:O(n^2)
空间复杂度:O(1)
哈希表
该题的思路就是枚举数组中的每一个x
,寻找数组中是否存在target-x
,问题的关键就在于如何寻找target-x
?
暴力解法中是使用for循环迭代查找,时间复杂度为O(n),使用哈希表来确定数组中是否存在目标元素则可以将复杂度降为O(1)。这样,我们创建一个哈希表,对于每一个x
,查询表中是否存在target-x
,然后将x
插入表中,这样可以保证x
不会和自己匹配。
由于本题,我们不仅需要知道元素是否遍历过,还要知道元素对应的下标,因此使用key-value
结构来存放,key
存元素,value
存下标,使用map
结构正合适。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map <int, int> map;
for (int i=0; i<nums.size(); i++) {
// 遍历当前元素,并在map中寻找是否有匹配的key
auto iter = map.find(target-nums[i]);
if (iter != map.end()) { // 哈希表中存在
return {iter->second, i};
}
// 如果没找到匹配对,就把访问过的元素和下标加入到map中
map.insert(pair<int, int>(nums[i], i));
}
return {};
}
};
时间复杂度:O(n)
空间复杂度:O(n)