set 和 map:
常见操作:
-
insert
-
find
-
erase
-
change (map)
map和set的底层实现为红黑树unordered_map
和unordered_set
的底层实现为哈希表
哈希表不管查找、插入、删除都是O(1)的时间复杂度。红黑树的时间复杂度为O(logn)。
哈希表的缺点是失去了数据的顺序性
数据的顺序性
-
数据集中的最大值和最小值
-
某个元素的前驱和后继
-
某个元素的floor和ceil
-
某个元素的排位rank
-
选择某个排位的元素select
290. 单词规律
给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。
这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。
示例1:
输入: pattern = "abba", str = "dog cat cat dog"
输出: true
示例 2:
输入:pattern = "abba", str = "dog cat cat fish"
输出: false
示例 3:
输入: pattern = "aaaa", str = "dog cat cat dog"
输出: false
示例 4:
输入: pattern = "abba", str = "dog dog dog dog"
输出: false
说明:
你可以假设 pattern 只包含小写字母, str 包含了由单个空格分隔的小写字母。
这道题用两个hash表来完成,分别将字符和单词都映射到当前的位置加1,注意这里需要加1就是为了避免默认映射值0。
如果字符和单词的映射值不相等,那么返回false
class Solution {
public:
bool wordPattern(string pattern, string str) {
vector<string>word;
int start = 0, len = 0;
for(int i = 0; i < str.length();i++)
{
if(str[i] != ' ')
len++;
else
{
string temp = str.substr(start, len);
word.push_back(temp);
start = i + 1;
len = 0;
}
}
string temp = str.substr(start, len);
word.push_back(temp);
int n = pattern.length();
if(n != word.size())
return false;
unordered_map<char, int>map1;
unordered_map<string, int>map2;
for(int i = 0; i < n; i++)
{
if(map1[pattern[i]] != map2[word[i]])
return false;
else
{
map1[pattern[i]] = i+1;
map2[word[i]] = i+1;
}
}
return true;
}
};
205. 同构字符串
给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。
所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。
示例 1:
输入: s = "egg", t = "add"
输出: true
示例 2:
输入: s = "foo", t = "bar"
输出: false
示例 3:
输入: s = "paper", t = "title"
输出: true
说明:
你可以假设 s 和 t 具有相同的长度。
这道题思路和上面的290相似,都是用两个map维护
class Solution {
public:
bool isIsomorphic(string s, string t) {
int n = s.length();
int m = t.length();
if(m != n)
return false;
int m1[256] = {0}, m2[256] = {0};
int i = 0;
while(i < n)
{
if(m1[s[i]] != m2[t[i]])
return false;
m1[s[i]] = i + 1;
m2[t[i]] = i + 1;
i++;
}
return true;
}
};
15. 三数之和
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
1、第一种思路使用map容器,空间复杂度O(n)。先排序时间复杂度O(nlogn), 然后两层循环遍历,根据和为0以及数组有序,当第一个元素大于0,就可以break循环。如果前后两个元素一样,也可以continue循环。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n = nums.size();
vector<vector<int>> ret;
if (n < 3)
return ret;
sort(nums.begin(), nums.end());
unordered_map<int, int>map;
for(int i = 0; i < n; i++)
map[nums[i]] = i;
for(int i = 0; i < n - 1; i++)
{
if(nums[i] > 0)
break;
if(i != 0 && nums[i] == nums[i-1])
continue;
for(int j = i + 1; j < n; j++)
{
if(j != i +1 && nums[j] == nums[j-1])
continue;
int target = -nums[i]-nums[j];
if(map.find(target) != map.end())
{
if(map[target] > i && map[target] > j)
{
vector<int> temp;
temp.push_back(nums[i]);
temp.push_back(nums[j]);
temp.push_back(target);
ret.push_back(temp);
}
}
}
}
return ret;
}
};
第二种方法,排序,使用双指针,时间复杂度O(n2),空间复杂度O(1)
16. 最接近的三数之和
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).]
和前面一道题类似,先排序然后使用双指针法,时间复杂度O(n2)
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
int n = nums.size();
int res = nums[0] + nums[1] + nums[2];
for(int i = 0; i < n-2; i++)
{
int l = i+1, r = n-1;
while(l < r)
{
int cur = nums[i] + nums[l] + nums[r];
if(abs(res-target) > abs(cur-target))
res = cur;
if(cur == target)
return res;
else if(cur < target)
l++;
else
r--;
}
}
return res;
}
};
49. 字母异位词分组
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。
建立一个map,以排序的字符串为键值,存储所有的字母异位词。
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>>ret;
if(strs.empty())
return ret;
unordered_map<string, vector<string>>mstrs;
for(auto s : strs)
{
string temp = s;
sort(temp.begin(), temp.end());
mstrs[temp].push_back(s);
}
for(auto m : mstrs)
ret.push_back(m.second);
return ret;
}
};
447. 回旋镖的数量
给定平面上 n 对不同的点,“回旋镖” 是由点表示的元组 (i, j, k) ,其中 i 和 j 之间的距离和 i 和 k 之间的距离相等(需要考虑元组的顺序)。
找到所有回旋镖的数量。你可以假设 n 最大为 500,所有点的坐标在闭区间 [-10000, 10000] 中。
示例:
输入:
[[0,0],[1,0],[2,0]]
输出:
2
解释:
两个回旋镖为 [[1,0],[0,0],[2,0]] 和 [[1,0],[2,0],[0,0]]
对坐标的每个i点,建立其他点到i点距离的map,遍历map,如果键值大于等于2,则到i点距离相等的点的组合为从n个点取两个点的排列组合。
class Solution {
public:
int numberOfBoomerangs(vector<vector<int>>& points) {
int n = points.size();
int ret = 0;
for(int i = 0; i < n; i++)
{
unordered_map<int, int>mapdis;
for(int j =0; j < n; j++)
{
if(i != j)
{
int temp = distance(points[i], points[j]);
mapdis[temp]++;
}
}
for(auto it:mapdis)
{
if(it.second >= 2)
ret += it.second*(it.second-1);
}
}
return ret;
}
int distance(const vector<int> &points1, const vector<int> &points2)
{
return (points1[0]-points2[0])*(points1[0]-points2[0])+
(points1[1]-points2[1])*(points1[1]-points2[1]);
}
};
219. 存在重复元素 II
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值最大为 k。
示例 1:
输入: nums = [1,2,3,1], k = 3
输出: true
示例 2:
输入: nums = [1,0,1,1], k = 1
输出: true
示例 3:
输入: nums = [1,2,3,1,2,3], k = 2
输出: false
第一种方法使用map
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
unordered_map<int, int> map;
for(int i = 0; i < nums.size(); i++)
{
if(map.find(nums[i]) == map.end())
map[nums[i]] = i;
else
{
if(i - map[nums[i]] <= k)
return true;
else
map[nums[i]] = i;
}
}
return false;
}
};
第二种方法使用set,同时维护一个大小为k+1的窗口
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
unordered_set<int> record;
for(int i = 0; i < nums.size(); i++)
{
if(record.find(nums[i]) != record.end())
return true;
record.insert(nums[i]);
if(record.size() >= k+1)
record.erase(nums[i-k]);
}
return false;
}
};
220. 存在重复元素 III
给定一个整数数组,判断数组中是否有两个不同的索引 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ。
示例 1:
输入: nums = [1,2,3,1], k = 3, t = 0
输出: true
示例 2:
输入: nums = [1,0,1,1], k = 1, t = 2
输出: true
示例 3:
输入: nums = [1,5,9,1,5,9], k = 2, t = 3
输出: false
这里有一个set的特殊用法lower_bound(num),返回set中大于等于num的数
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
set<long> ret;
for(int i = 0; i < nums.size(); i ++)
{
auto it = ret.lower_bound(long(nums[i]) - t);
if(it != ret.end() && (*it - nums[i]) <= t)
return true;
ret.insert(nums[i]);
if(ret.size() > k)
ret.erase(nums[i-k]);
}
return false;
}
};