242. 判断字符串 t 是否是字符串 s 的字母异位词
使用哈希表记录每个字符的出现次数。遍历 s,每遍历到一个字符,则将对应的次数 + 1。再遍历 t,每遍历到一个字符,则将对应的次数 - 1。最后若表中所有字母的出现次数均为 0,则判断结果为真。
383. 判断字符串 t 是否是字符串 s 的子串的字母异位词
与上一题类似,判断条件改为所有字母的出现次数大于等于 0 即可。
49. 对字符串集合按照字母异位词进行分组
- 将排序后的字符串作为 key,字符串列表作为 value,使用哈希表进行分组。
- 对每个字符串使用长度为 26 的数组记录每个字符的出现次数,将该数组作为 key,字符串列表作为 value,使用哈希表进行分组。
438. 寻找字符串 t 中所有的是字符串 s 的字母异位词的连续子串
将长度为 s . l e n g t h s.length s.length 的滑动窗口逐渐右移即可,每次移动更新每个字母的出现次数并与 s 中相应的出现次数进行比较,若是字母异位词则保存结果,可以使用状态压缩或记录数量不同的字母个数使比较的时间复杂度降到 O ( 1 ) O(1) O(1)。
349. 求两个数组的交集(去重)
- 使用两个
unordered_set
,时间复杂度 O ( n + m ) O(n + m) O(n+m),空间复杂度 O ( n + m ) O(n + m) O(n+m)。 - 对两个数组进行排序(或转化成
multiset
)和合并,合并时只取相等元素,且要确认上一个插入的元素与要插入的元素不相等以避免重复,时间复杂度 O ( n l o g ( n ) + m l o g ( m ) ) O(nlog(n) + mlog(m)) O(nlog(n)+mlog(m)),空间复杂度 O ( l o g ( n ) + l o g ( m ) ) O(log(n) + log(m)) O(log(n)+log(m))。
350. 求两个数组的交集(不去重)
- 使用一个
unordered_map
记录较短数组中每个元素出现的次数。对较长数组中的每一个元素,若unordered_map
中记录的该元素数量大于 0,则将该元素插入结果数组并将unordered_map
中的计数减 1。时间复杂度 O ( n + m ) O(n + m) O(n+m),空间复杂度 O ( m i n ( n , m ) ) O(min(n, m)) O(min(n,m)),该方法可适用于某一个数组过长无法全部加载到内存的情况。 - 对两个数组进行排序(或转化成
multiset
)和合并,合并时只取相等元素,此时无需检查是否重复。时间复杂度 O ( n l o g ( n ) + m l o g ( m ) ) O(nlog(n) + mlog(m)) O(nlog(n)+mlog(m)),空间复杂度 C++ 直接返回结果数组时 O ( 1 ) O(1) O(1),其他语言 O ( m i n ( n , m ) ) O(min(n, m)) O(min(n,m))。
202. 快乐数(定义:对一个数的每个十进制位求平方和作为新值,重复执行直到该数为 1)
- 使用集合记录模拟过程中出现过的数,若某一次迭代导致迭代路径有环,则判断不是快乐数。
- 类似快慢指针,快指针一次迭代两步,慢指针一次迭代一步,若二者指向了相同的值则表示迭代路径有环。
- 可证明唯一可能出现的环是 4→16→37→58→89→145→42→20→4,检查是否出现了这些数即可。
1. 两数之和(返回下标二元组的数组)
使用哈希表记录遍历过程中已经遇到的数的下标,每次迭代检查是否已经记录了互补的数即可。须注意要先检查后添加记录,以避免返回两个相同的下标。
15. 三数之和(返回值的三元组的数组)
首先排序数组。使用两层循环,第一层遍历第一个数,需要保证每一次循环的数都不一样。第二层使用双指针,靠前的指针遍历第二个数,同样需要保证每一次循环的数都不一样,每一次靠前指针的移动都可以确定一个第三个数的值,从后往前移动靠后的指针找到第三个数即可。需要注意双指针不能相遇,一旦检测到相遇,可以推出靠前的指针没有必要继续向前移动,直接开始下一次的第一层遍历。
第二层循环查找第三个数也可以采用哈希表的方法,此时哈希表记录每个元素出现的初始位置,具体见下面代码:
using Iterator = vector<pair<int, int>>::iterator;
Iterator prev(Iterator it) {
return --it;
}
Iterator next(Iterator it) {
return ++it;
}
int count(Iterator it) {
return next(it)->second - it->second;
}
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
const int n = nums.size();
if (n < 3)
return {};
if (!is_sorted(nums.begin(), nums.end()))
sort(nums.begin(), nums.end());
vector<pair<int, int>> index;
index.push_back({nums[0], 0});
for (int i = 1; i < n; ++i) {
if (nums[i] != index.back().first)
index.push_back({nums[i], i});
}
index.push_back({INT_MAX, n});
auto e = prev(index.end());
unordered_map<int, Iterator> find;
for (auto it = index.begin(); it != index.end(); ++it) {
find[it->first] = it;
}
vector<vector<int>> result;
for (auto i = index.begin(); i != e; ++i) {
int i_count = count(i) - 1;
for (auto j = i_count > 0 ? i : next(i); j != e; ++j) {
int j_count = i == j ? i_count - 1 : count(j) - 1;
int thirdNum = -i->first - j->first;
if (find.find(thirdNum) == find.end())
continue;
auto k = find[thirdNum];
if (k->second < j->second)
break;
else if (k != j || j_count > 0)
result.push_back({i->first, j->first, k->first});
}
}
return result;
}
};
18. 四数之和(返回值的四元组的数组)
与三数之和类似,加一层循环即可。
454. 四数之和(从等长的四个数组(每个数组中或有重复的数)中各选一个数,返回符合条件的四元组的数量)
设四个数组分别是 A,B,C,D,设一个哈希表 countAB,记录 A 和 B 中元素的各种和的出现次数。
对 C 和 D 的笛卡尔积进行遍历,若在 countAB 中能查找到 -(C[i] + D[j]),则将最终结果自增 countAB[-(C[i] + D[j])] 即可。
如此可实现
O
(
n
2
)
O(n^2)
O(n2) 的时间复杂度和空间复杂度。