目录
两数之和
暴力
二分
哈希
字母异位词分组
unordered_map + 排序
unordered_map + 计数
最长连续序列
unordered_set + 跳过前驱
排序 + dp
两数之和
1. 两数之和 - 力扣(LeetCode)
暴力
O(n^2) 两层循环
class Solution {
public:
vector
int n = nums.size();
for (int i = 0; i < n - 1; ++i)
for (int j = i + 1; j < n; ++j)
if (nums[i] + nums[j] == target)
return {i, j};
return {}; // 返回空数组
}
};
二分
O(nlogn)
O(nlogn) 整数二分 整数二分需要注意边界,防止下取整的坑(死循环)
排序 O(nlogn)
外层for O(n) * for 循环里二分 O(logn) = O(nlogn)
详细解释下第 32 行
if (a[i].x == a[l].x) l++; // 避免同一个元素, 索引++(最关键)
比如 i == 2, l == 4,2 + 4 == 6,而假设此时双方的值都是3,3 + 3 == 6
那么就会输出同一个元素的下标,需要索引 + 1
AC 代码
#include
using namespace std;
struct node {
int v, x; // value 和 index
}a[10010];
bool cmp(node x, node y)
{
return x.v < y.v;
}
class Solution {
public:
vector
int n = nums.size();
for (int i = 0; i < n; ++i)
a[i].v = nums[i], a[i].x = i; // 先结合
sort(a, a + n, cmp); // 再排序(结构体数组)
// 遍历
for (int i = 0; i < n; ++i) {
int l = 0, r = n - 1;
while (l < r) { // 二分
int m = (l + r) >> 1;
if (a[m].v >= target - a[i].v) // target - a[i].v 为要查找的值
r = m;
else
l = m + 1;
}
// 退出 while 后, l == r
if (a[i].v + a[l].v == target) {
if (a[i].x == a[l].x) l++; // 避免同一个元素, 索引++(最关键)
return {a[i].x, a[l].x}; // 返回下标
}
}
return {}; // 返回空数组
}
};
哈希
O(n)
知识
map
map
h[key] = val;
//等价于
h.insert(make_pair(key, val));
for(map
cout< size/empty/clear //元素个数,判空,清空 begin / end //指向开始 / 结束位置的指针 insert(x) //将元素x插入集合(x为二元组) erase(x) //删除所有等于x的元素(x为二元组) erase(it) //删除it指向的元素(it为指向二元组的迭代器) find(k) //查找键为k的二元组的位置, 若不存在, 返回尾指针 .end() C++ map和unordered_map详解-腾讯云开发者社区-腾讯云 (tencent.com) AC 代码 class Solution { public: vector int n = nums.size(); unordered_map for (int i = 0; i < n; ++i) { auto it = h.find(target - nums[i]); // 查找这个键 -- 元素大小 if (it != h.end()) // 找到了该元素 return {i, it->second}; // 返回下标 //h.insert(make_pair(nums[i], i)); // 插入键值对 h[nums[i]] = i; // 插入键值对 } return {}; // 返回空数组 } }; 字母异位词分组 49. 字母异位词分组 - 力扣(LeetCode) 知识 1)emplace_back 与 push_back 一文弄清楚 push_back 和 emplace_back 的区别-emplace_back和push_back有区别吗 (51cto.com) 2)unordered_map 与 map map和unordered_map区别及其优缺点 - 己平事 - 博客园 (cnblogs.com) map、multimap 容器都会自行根据键的大小对存储的键值对进行排序 3)代码中,两个要注意的点 (一)引用传递 比 迭代器 快很多(访问vector // 引用传递 for (string& str : strs) // 迭代器 for (vector (二)emplace_back 比 push_back 快 20% unordered_map + 排序 O(n*k*logk) n -- strs中字符串数量 k 单个字符串最大长度 ≈ 5*1e6 class Solution { public: vector unordered_map vector // 遍历字符串数组 strs for (string& str : strs) { // 引用传递 快很多 string key = str; sort(key.begin(), key.end()); s[key].push_back(str); } // for (vector // string key = *it; // 键 // sort(key.begin(), key.end()); // 对键排序 // s[key].push_back(*it); // 键 -- 排序后的值 // } for (auto t : s) ans.push_back(t.second); // 插入vector // for (auto it = s.begin(); it != s.end(); ++it) // ans.push_back(it->second); return ans; } }; unordered_map + 计数 O(n*k) 排序 --> 计数,变相得到了每个字符串按字典序的排序(隐含的是:int 和 char 之间的 隐式转换 但是有个疑问,如果一个字母出现100次,' ' + 100 == 132,超过了ASCII的127范围,超过范围阶段,可能和前面的冲突才对 可能是样例太少了 注意:& 比 不用&,快很多,避免将元素的值复制到新的变量中 class Solution { public: vector // 键--已排序字符串 值--未排序字符串 组成的数组 unordered_map vector for (string& str : strs) { // str 每个字符串 // string count = string(26, ' '); string count(26, ' '); for (char& c : str) // c 一个字符 count[c - 'a']++; s[count].push_back(str); // 插入键值对 } for (auto& t : s) // t--unordered_map ans.push_back(t.second); // 插入 vector return ans; } }; 最长连续序列 128. 最长连续序列 - 力扣(LeetCode) 知识 size/empty/clear //元素个数 判空 清空 begin/end //开始和结束位置 inset(x) //元素x插入集合 erase(x) //删除所有值为x的元素 erase(it) //删除it迭代器指向的元素 find(x) //查找x在集合的位置,不存在则返回end count(x) //统计x元素的个数 lower_bound(x) //返回大于等于x的最小元素位置 upper_bound(x) //返回大于x的最小元素位置 思路 set 用于去重 unordered_set,不需要有序,所以用 unordered 存在前驱则跳过 unordered_set + 跳过前驱 O(n) 外层循环 for,因为“存在前驱,则跳过”,数组中每个数,只会进入 内层 while 循环 1 次 所以外层 O(n),内层 O(1) class Solution { public: int longestConsecutive(vector int ans = 0; // 初始0 有可能空数组 unordered_set for (const int& num : nums) // 引用传递 s.insert(num); for (auto& num : s) { // 遍历哈希表 if (s.count(num - 1)) continue; // 存在前驱 -- 跳过当前 int currentNum = num, currentLen = 1; // 当前长度 1 while (s.count(currentNum + 1)) { currentLen++; // 长度 +1 currentNum++; // 值 +1 } ans = max(ans, currentLen); } return ans; } }; 排序 + dp 很好奇,为什么 O(nlogn) 的复杂度,要比上面的 O(n) 快得多........ class Solution { public: int longestConsecutive(vector if (!nums.size()) return 0; // 避免空数组 sort(nums.begin(), nums.end()); // 先排序 vector nums2.push_back(nums[0]); for (int i = 1; i < nums.size(); ++i) if (nums[i] != nums[i - 1]) // 再去重 nums2.push_back(nums[i]); int n = nums2.size(), ans = 1; if (n == 0) return 0; // 避免空数组 vector for (int i = 1; i < n; ++i) { if (nums2[i] == nums2[i - 1] + 1) dp[i] = dp[i - 1] + 1; ans = max(ans, dp[i]); } return ans; } }; /* 含义:dp[i] 以第 i 个数结尾的最大长度(下标 0 开始) 递推式:if (nums[i] == nums[i - 1] + 1) dp[i] = dp[i - 1] + 1; 初始化:dp[0...n-1] = 1 遍历顺序:nums[] 0...n-1 打表检查 */ 精彩文章
发表评论