【力扣】hot100系列(一)哈希部分解析(多解法+时间复杂度分析)
本专栏文章持续更新,新增内容使用蓝色表示。
1. 两数之和 - 力扣(LeetCode)
梦开始的地方,不多言。
49. 字母异位词分组 - 力扣(LeetCode)
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
【解法一】
根据题目描述,不难想到可以对每一位进行排序,排序之后,字母异位词可以拥有相同的key值。
比如"ate","eat","tea"排序之后都为 aet,所以可以循环对每一位进行处理,排序后相同的字母放在一起,可以使用 map,key 值为字符串,value 值由于要存储多个字符串,所以使用 vector<string>。
class Solution {
public:vector<vector<string>> groupAnagrams(vector<string>& strs) {map<string, vector<string>> ans;// 遍历每一位string key;for (auto& value : strs) {key = value;sort(key.begin(), key.end()); // 排序ans[key].push_back(value);}// 由于返回类型为vector<vector<string>>,所以此处需要处理一下vector<vector<string>> res;for (auto& pair : ans) {res.push_back(pair.second);}return res;}
};
注意:C++里sort 函数返回值类型为 void,sort(key.begin(), key.end());会直接改变key。
时间复杂度为 O(n * L * log L)
n 为输入数组 strs 的长度(字符串的个数), L 为数组中字符串的平均长度。其中 L*Log L 是sort函数的时间复杂度。
以第一个循环为例:
for (auto& value : strs) { // O(n)key = value; # O(L)sort(key.begin(), key.end()); // O(L*Log L)ans[key].push_back(value); // O(L)}
时间复杂度取最大的,O(n*L) 和 O(n*L*Log L) 中,后者的更大,与第二个循环相比依旧是 O(n * L * log L)更大,所以这个函数整体的时间复杂度为 O(n * L * log L)。
优化方法:使用 unordered_map<string, vector<string>> ans; 字面意思,这个类型不会自动排序,所以也会更快一点。
空间复杂度 O(n * L),存储所有字符串
【解法二】使用字符计数编码
看到这道题,我的第一想法有考虑过使用所有的字符之和作为key值,显然这并不合适,不过可以使用字符序列作为key值。
原始的key值是"00000000000000000000000000",共26位,代表26个英文字母。从第0位开始,依次代表a,b,c,d......,出现一个字符,对应位加1。
以abc为例,想要增加对应位的值,可以依次遍历abc字符串,对每一位减去字符‘a’,a-a=0, b-a=1, c-a=2,使用这种方法可以找到对应位的下标,然后++即可。
"abc" → "11100000000000000000000000"
"ddd" → "00030000000000000000000000"
class Solution {
public:vector<vector<string>> groupAnagrams(vector<string>& strs) {unordered_map<string, vector<string>> ans;for (auto& value : strs) {string key = string('0', 26);for (auto v : value) {key[v - 'a']++;}ans[key].push_back(value);}vector<vector<string>> res;for (auto& pair : ans) {res.push_back(pair.second);}return res;}
};
这种方法相较于上一解法,没有使用sort函数,所以时间复杂度上有优化。
依旧假设 n 为输入数组 strs 的长度(字符串的个数), L 为数组中字符串的平均长度。
for (auto& value : strs) { // O(n)string key = string('0', 26); // O(1)for (auto v : value) { // O(L)key[v - 'a']++; // O(1)}ans[key].push_back(value); // O(L)}
时间复杂度: O(n * L),空间复杂度: O(n * k) 。
128. 最长连续序列 - 力扣(LeetCode)
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例1 :
输入:nums = [100,4,200,1,3,2,4,4]
输出:4
示例2:
nums=[]
0
解法一
class Solution {
public:int longestConsecutive(vector<int>& nums) {if (nums.size() <= 1) return nums.size();sort(nums.begin(), nums.end());int ans = 1;int current = 1; // 当前连续序列长度for (int i = 1; i < nums.size(); i++) {// 跳过完全相同的元素if (nums[i] == nums[i - 1]) {continue;}// 连续数字if (nums[i] == nums[i - 1] + 1) {current++; # 当然也可以选择每一步都更新} else {// 序列中断,更新,重置ans = max(ans, current);current = 1;}}ans = max(ans, current); # 不要忘记这一步return ans;}
};
时间复杂度:sort函数的时间复杂度为O(n*log n),循环的时间复杂度为O(n),所以最终的为O(n*log n)。
解法二
加了去重。
class Solution {
public:int longestConsecutive(vector<int>& nums) {// 第一个想法排序sort(nums.begin(), nums.end());// 但是要求O(n)级别,双指针?// 发现问题,排序之后是不完全递增// 想办法消除nums.erase(unique(nums.begin(), nums.end()), nums.end());if (nums.size() <= 1)return nums.size();int pre = 0, end = 1;int ans = 1;for (end; end < nums.size();end++) {if (nums[end] - nums[end - 1] != 1) {ans = max(ans, end - pre);pre = end;} }ans = max(ans, end - pre);return ans;}
};
同上时间复杂度为O(n*log n)。
如有问题或建议,欢迎在评论区中留言~