前言
来自 英雄哪里出来 的一个 免费 集训,每天
5
5
5 点打卡学习算法(我是为了卷吗,主要是想早起 😏),希望能坚持下去。这里用来复盘每天都的打卡题目。
今日份知识点:哈希
xxxxx。
一、题目
题目 | 难度 |
---|---|
442. 数组中重复的数据 | ⭐️ |
2068. 检查两个字符串是否几乎相等 | ⭐️ |
2283. 判断一个数的数字计数是否等于数位的值 | ⭐️ |
884. 两句话中的不常见单词 | ⭐️ |
二、算法思路
1、数组中重复的数据
(1)计数数组: 建一个计数数组, 遍历一遍数组, 记录每个元素的出现次数, 之后再遍历一遍计数数组, 把出现次数等于 2 的元素取出来.
时间复杂度:
O
(
2
n
)
O(2n)
O(2n)
class Solution {
public:
vector<int> findDuplicates(vector<int>& nums) {
vector<int> cnt(100010);
for (int i = 0; i < nums.size(); ++ i) {
++ cnt[nums[i]];
}
vector<int> ret;
for (int i = 0; i < cnt.size(); ++ i) {
if (cnt[i] == 2) {
ret.push_back(i);
}
}
return ret;
}
};
(2)原地哈希: 由于题目中给出数组的元素只会出现 1 次或者 2 次, 所以我们可以采用取反作为标记, 记录元素
n
u
m
s
[
i
]
nums[i]
nums[i] 是否出现过. 对于每一个
n
u
m
s
[
i
]
nums[i]
nums[i] 我们将其映射到下标
n
u
m
s
[
i
]
−
1
nums[i]-1
nums[i]−1 的位置去. 随后遍历数组, 如果
n
u
m
s
[
i
]
nums[i]
nums[i] 是正数,那么说明
i
+
1
i+1
i+1 这个数在已遍历的元素中没出现过,那么将下标为
n
u
m
s
[
i
]
−
1
nums[i]-1
nums[i]−1 的元素取反,表示已经出现过一次了;如果是负数,那么说明该元素已经出现过一次了,那么我们将其放进答案数组中。
注意:因为元素可能是负数,所以在取下标的时候,需要取绝对值。
时间复杂度:
O
(
n
)
O(n)
O(n)
class Solution {
public:
vector<int> findDuplicates(vector<int>& nums) {
int n = nums.size();
vector<int> ret;
for (int i = 0; i < n; ++ i) {
int t = nums[i];
if (nums[abs(t) - 1] < 0) {
ret.emplace_back(abs(t));
}
nums[abs(t) - 1] *= -1;
}
return ret;
}
};
2、检查两个字符串是否几乎相等
(1)计数数组:建两个长度为 26 的计数数组,分别遍历两个字符串,记录每一个字符串中每一个字符出现的频率,最后遍历一遍计数组,如果频率之差超过 3,那么返回 false,否则返回 true。xxxxxxx
时间复杂度:
O
(
2
∗
n
+
26
)
O(2*n+26)
O(2∗n+26)
class Solution {
public:
bool checkAlmostEquivalent(string word1, string word2) {
vector<int> cnt1(26), cnt2(26);
for (auto x: word1) {
++ cnt1[x - 'a'];
}
for (auto x: word2) {
++ cnt2[x - 'a'];
}
for (int i = 0; i < cnt1.size(); ++ i) {
if (abs(cnt1[i] - cnt2[i]) > 3)
return false;
}
return true;
}
};
(2)优化空间:遍历第一个字符串时,对计数数组 +1;遍历第二个字符串时,对计数数组 -1;最后遍历计数数组,如果频数差大于 3,返回 false,否则返回 true。
class Solution {
public:
bool checkAlmostEquivalent(string word1, string word2) {
vector<int> cnt(26);
for (auto x: word1) {
++ cnt[x - 'a'];
}
for (auto x: word2) {
-- cnt[x - 'a'];
}
for (int i = 0; i < cnt.size(); ++ i) {
if (abs(cnt[i]) > 3)
return false;
}
return true;
}
};
3、判断一个数的数字计数是否等于数位的值
(1)一个计数数组,记录字符串中每个字符出现的次数,(将字符
i
i
i 映射到
i
−
0
i-0
i−0 的下标计数数组中,最后遍历一遍计数数组,如果发现不符合题目要求即返回 false,最后返回 true。
时间复杂度:
O
(
2
n
)
O(2n)
O(2n)
class Solution {
public:
bool digitCount(string num) {
int n = num.size();
vector<int> cnt(10);
for (int i = 0; i < n; ++ i) {
++ cnt[num[i] - '0'];
}
for (int i = 0; i < n; ++ i) {
if (cnt[i] != num[i] - '0')
return false;
}
return true;
}
};
4、两句话中的不常见单词
(1)将 s1 和 s2 拼接起来,把每个单词切分出来,统计出现的频数。如果出现的频数为 1,那么说明该单词在其中一句话出现过,在另一句话中没出现,添加到结果数组中。
时间复杂度:
O
(
n
)
O(n)
O(n)
class Solution {
public:
vector<string> uncommonFromSentences(string s1, string s2) {
unordered_map<string, int> cnt;
string s = s1 + " " + s2;
for (int i = 0; i < s.size(); ++ i) {
string str = "";
while (i < s.size() && s[i] != ' ') str += s[i ++];
++ cnt[str];
}
vector<string> ret;
for (auto x: cnt) {
if (x.second == 1)
ret.emplace_back(x.first);
}
return ret;
}
};