49. 字母异位词分解

1、题解
这道题如果拿两个字符串进行两两比较,依次遍历数组,是不是时间复杂度就是O(N2);但是看题解有提到“字母异位词是可以重新排列形成彼此的”,那可不可以尝试把每个字符串都先排列之后作为key值,字母异位词不就可以匹配成功,然后插入到哈希表里面吗?
2、代码实现
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
//搞个tmp字符串排序,排序后的结果作为key值,原字符串作为value,匹配成功插入hash表
unordered_map<string, vector<string>> hash;
for(auto &s : strs)
{
string tmp = s;
sort(tmp.begin(), tmp.end());
hash[tmp].push_back(s);
}
//提取hash表里的vector<string>
vector<vector<string>> ret;
for(auto &[x, y] : hash)
{
ret.push_back(y);
}
return ret;
}
};
128. 最长连续子序列


1、题解
这道题要我们找到数字连续的最长序列,并且时间复杂度为O(N),看了上面几个实例,会发现给的数组是有可能出现重复的数字的,所以我们在找数字连续的最长序列时:应该先去重,把所有元素都扔到哈希表。
不需要先给它们排序,只要遍历这个哈希表,如果元素的前一个元素(元素值-1)存在,说明这个元素就不是连续序列的开头,那就直接跳过;如果元素的前一个元素(元素值-1)不存在,,说明这个元素就是连续序列的开头,接下来就一直查找“元素值+1”的元素是否存在,并且更新长度。
2、代码实现
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> s(nums.begin(), nums.end());
int max_len = 0;
for(auto e: s)
{
if(s.count(e-1) == 0)//e是序列开头
{
int cur = e;
int len = 1;
while(s.count(cur+1))//计算数字连续序列长度
{
len++;
cur++;
}
max_len = max(max_len, len);//更新最长连续长度
}
}
return max_len;
}
};
分析算法复杂度,哈希查找时间复杂度是O(1),外层只有一个for循环,总的时间复杂度应该是O(N)。
283. 移动零

1、题解
这道题有个很关键的信息:必须保持非0元素的相对顺序,如果用双指针,一个在开头,一个在结尾,那么在交换的过程中势必会改变元素的相对顺序。
我们可以将数组分为三部分,【非0区间、0区间、未被遍历区间】,当我遍历的元素不是0时,只要把这个元素加入到非0区间末尾,就是交换nums[cur] 和 nums[dst++]。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
//数组分为三部分:非0, 0,未被遍历的
int dst = -1;
for(int cur = 0; cur < nums.size(); cur++)
{
if(nums[cur])
{
swap(nums[++dst], nums[cur]);
}
}
}
};
11. 盛水最多的容器


1、题解
这道题要我们找区间面积最大的面积,那我们要先搞清楚面积怎么算,是不是宽度(下标差)* 高度(下标对应值小的)。如果说用暴力枚举,每找一个元素,就和后面的所有元素进行计算面积,那算法复杂度就是O(N2)了。
按照上面的图,我先拿[6, 2, 5, 4, 8]这个区间,假设我先计算最左边和最右边的面积v,按照暴力算法,是不是还要枚举6和 [2, 5, 4] 的面积,但是有必要吗?首先 面积 = 高度 * 宽度,你考虑6和其他数字,宽度一定减小,高度呢?高度要么减小,要么不变,那面积是不是一定减小,那就没有必要再继续枚举6和 [2, 5, 4] 的面积。所以我们在做这道算法题时,是可以舍弃掉高度小的那个数的。
2、代码实现
class Solution {
public:
int maxArea(vector<int>& height) {
int left = 0, right = height.size() - 1;
int max_area = 0;
while(left < right)
{
int v = (right - left) * min(height[left], height[right]);
max_area = max(max_area, v);
if(height[left] < height[right]) left++;//高度小的没必要考虑和其他元素构成的区间面积
else right--;
}
return max_area;
}
};
15. 三数之和

1、题解
这道题要我们找到的是三个不用下标的数,它们的和为0的三元组,看示例1可以发现,有重复的三元组是需要去重的。
(1)暴力算法:我们第一眼想到的就是我固定一个数,遍历,再固定一个数,再遍历,然后求和,这样的话时间复杂度肯定是O(N3)
(2)暴力优化:如果我们把数组排个序,然后呢还是遍历,把结果扔到哈希表去重,那这样时间复杂度还是O(N3)。
(3)我们可以在排序后,先固定一个数a,然后在剩下的区间里找两个数的和为 target = 0 - a,这样一来不就变成了双指针问题了吗,时间复杂度降到O(N2)了;
不过这里要注意,之前我们做双指针问题时,只要有一种情况成立就可以返回,这道题是要找到所有的三元组哦,所以要把剩下的区间都遍历完;
并且这道题要求不能重复,你成立的三元组里是不能存在相同的三元组,可以直接把这个三元组扔到哈希表里去重;还有一种办法,我们不是把数组排序了吗,那当我找到一组成立的结果时,如果有重复的数字,也是在结果附近,我直接比较附近的数的大小,移动我的 left 指针和 right 指针不就好了;除了子区间不能重复,固定数是不是也不能重复,这个地方也要注意

2、代码实现
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ret;
sort(nums.begin(), nums.end());
int i = 0;
while(i < nums.size() - 2)
{
int target = 0 - nums[i];//固定一个数,在后面的区间使用双指针
int left = i + 1, right = nums.size() - 1;
while(left < right)
{
int tmp = nums[left] + nums[right];
if(tmp > target) right--;
else if(tmp < target) left++;
else//找到目标两个下标了,进行插入,然后去重
{
ret.push_back({nums[i], nums[left++], nums[right--]});
while(left < right && nums[left] == nums[left-1]) left++;
while(left < right && nums[right] == nums[right+1]) right--;
}
}
i++;
//注意i也要去重
while(i < nums.size() - 2 && nums[i] == nums[i-1]) i++;
}
return ret;
}
};
362



被折叠的 条评论
为什么被折叠?



