Leetcode hot 100刷题之路(day 1)
两数之和
第一种解法:显而易见暴力解法 时间复杂度O(n^2)明显过高
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
for(int i=0;i<nums.size();i++)
{
for(int j=i+1;j<nums.size();j++)
{
if(nums[i]+nums[j]==target)return {i,j};
}
}
return {};
}
};
第二种解法 可以用空间换时间 用哈希表存储数据,因为之前第一种解法每当遍历到一个数,都要判断一个个判断和之前的数相加是否为target。使用哈希表便只需要O(1)时间
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> mp;
mp[nums[0]]=0;
for(int i=1;i<nums.size();i++)
{
if(mp.count(target-nums[i]))return{mp[target-nums[i]],i};
else mp[nums[i]]=i;
}
return {};
}
};
字母异位词分组
第一种做法:使用哈希,首先要给传入的字符串排序,如果排序后已经存在于哈希表了,那就存入。这题主要要注意的vector<vector<string>>如何存入 时间复杂度O(nklogk) n是字符串数量,k是字符串最大长度,需要klogk时间排序以及o(1)存入哈希表
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>> vec;
int cnt=0;
unordered_map<string,int> mp;
for(int i=0;i<strs.size();i++)
{
string str=strs[i];
sort(str.begin(),str.end(),less<char>());
if(mp.count(str)!=1)
{
mp[str]=cnt++;
vector<string> sts(1,strs[i]);
vec.emplace_back(vector<string>{strs[i]});//emplace_back(sts)
}//新开一个异位词列表
else
{
vec[mp[str]].emplace_back(strs[i]);
}
}
return vec;
}
};
最长连续子序列
想要找到最长子序列,其实就是对每一个数x,查询数组中是否存在 x+1,如果有,再次查询是否有x+2.可以用哈希优化搜寻过程为O(1),但是,外层需要遍历n个数,内层也需要匹配O(n)次,但其中有可以优化的过程,如果x匹配过了 那么x+1就不需要再次判断,因为肯定比x匹配过的短。所以只需要对不存在x-1的x进行匹配即可,时间复杂度O(n).代码如下
基本知识:unordered_set无序,但查找单个元素快于set
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> st;
for(auto &it:nums) st.insert(it);
int max_num=0;
for(auto &it:st)
{
if(st.count(it-1)==0)
{
int now_num=it+1;
int now_length=1;
while(st.count(now_num))
{
now_num++;
now_length++;
}
max_num=max(max_num,now_length);
}
}
return max_num;
}
};
移动零
目的是让数组分为两个区域,非零区域和零区域。那么可以使用双指针来维护这两块区域,第一块区域是非零区,只需要一个双指针指向尾部+1的位置。第二块是待处理区域,待处理区域也用一个指针维护,两块区域之间就是零区域。当右指针指向非零,需要移动,不然会进入零区域,当指向零,不需要操作,只需要指向下一块区域。时间复杂度O(n)
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int lp=0,rp=0;
while(rp<nums.size())
{
if(nums[rp])
{
swap(nums[lp],nums[rp]);
lp++;
}
rp++;
}
}
};
乘最多水的容器
利用双指针指向容器的两边。暴力的思想就是针对每一个点,遍历后面的所有点,时间复杂度O(n^2)明显过于复杂。我们利用双指针可以优化到O(n),面积是由长度*两者间最小的高度确定的。那么当缩短长度时,只有高度变高才有可能变大。所以我们每次只缩短高度低的那一侧指针即可。
class Solution {
public:
int maxArea(vector<int>& height) {
int lp=0,rp=height.size()-1;
int max_num=0;
while(rp>lp)
{
int high=min(height[lp],height[rp]);
max_num=max(max_num,(rp-lp)*high);
if(height[lp]<height[rp])lp++;
else rp--;
}
return max_num;
}
};
三数之和
其实也有点像双指针,都是利用两个指针来确定区域。如果这题直接做是O(n^3),考虑优化,先进行sort排序,空间复杂度O(logN)_递归调用。我们可以观察到,在最外层i确定之后,j增加,k只能减少。所以可以减少到O(n^2).而还有重要的一点,i和j必须保证和上一次不同。k不需要,因为i,j不同后,k必然不同.同时,这个保证不相同的代码必须要注意有没有超出上限
学习到的知识:代码要有鲁棒性,保证不会超出内存。emplace_back需要vector<int>{a,b,c}而push_back的插入只需要{a,b,c}.代码如下
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> vec;
sort(nums.begin(),nums.end(),less<int>());
for(int i=0;i<nums.size()-2;i++)
{
while(i>0&&nums[i]==nums[i-1]&&i<nums.size()-2)i++;
int k=nums.size()-1;
int target=nums[i]*-1;
for(int j=i+1;j<nums.size()-1;j++)
{
while(j>i+1&&nums[j]==nums[j-1]&&j<nums.size()-1)j++;
while(j<k&&nums[j]+nums[k]>target)
{
k--;
}
if(j==k)break;
if(nums[j]+nums[k]==target)vec.emplace_back(vector<int>{nums[i],nums[j],nums[k]});
}
}
return vec;
}
};
接雨水 典中典
第一种解法,维护双指针,分别在0,size()-1,维护两个变量,分别是左最高,右最高。每次只有这两个变量的最小值才能更新ans 时间复杂度O(n)
class Solution {
public:
int trap(vector<int>& height) {
int ans=0;
int lp=0,rp=height.size()-1;
int l_max=0,r_max=0;
while(lp<rp)
{
l_max=max(l_max,height[lp]);
r_max=max(r_max,height[rp]);
if(l_max<r_max)
{
ans+=l_max-height[lp];
lp++;
}
else
{
ans+=r_max-height[rp];
rp--;
}
}
return ans;
}
};
做法2:直接一开局就搜寻每个点的leftmax和rightmax。然后遍历一遍就好,其实差不多
做法3:单调栈。这个想法挺好的
无重复字符最长子串
考虑字符串X1X2X3X4X5,当1-3均不重复,4出现重复,可能是任意一个1-3的字母,常规解法是从2开始继续遍历,但其实可以从4继续遍历。时间复杂度O(N)N为字符串长度,左右指针分别遍历了整个字符串一次。代码如下
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_set<char> st;
int max_num=0;
int rp=0;
for(int i=0;i<s.size();i++)
{
if(i)st.erase(s[i-1]);
while(rp<s.size()&&st.count(s[rp])!=1)
{
st.insert(s[rp]);
rp++;
}
max_num=max(max_num,rp-i);
}
return max_num;
}
};
找到字符串中所有字母异位词
特判一下大小。这道题用了数组的做法,判断两者的字母数组是否相同。
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int lens=s.size(),lenp=p.size();
if(lens<lenp)return vector<int>();
vector<int> scount(26,0);
vector<int> pcount(26,0);
vector<int >ans;
for(int i=0;i<lenp;i++)
{
scount[s[i]-'a']++;
pcount[p[i]-'a']++;
}
if(scount==pcount)ans.emplace_back(0);
for(int i=1;i<lens-lenp+1;i++)
{
scount[s[i-1]-'a']--;
scount[s[i+lenp-1]-'a']++;
if(scount==pcount)ans.emplace_back(i);
}
return ans;
}
};
其实也可以只用一个数组。s字符串中出现+1,p字符串中出现-1.如果都为0,那么便压入
和为K的子数组
暴力做法 超时O(n^2)
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int count=0;
for(int i=0;i<nums.size();i++)
{
int sum=0;
for(int j=i;j<nums.size();j++)
{
sum+=nums[j];
if(sum==k)count++;
}
}
return count;
}
};
前缀和+哈希表优化做法
其实就是连续子序列为k 即sum[i]-sum[j]=k我们只需要求出,到底有几个j就好,那么其实sum[j]=sum[i]-k,只要用map记录sum[j]的个数即可,时间复杂度O(n)
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int,int>mp;
mp[0]=1;//因为可能k直接等于nums中某个元素
int count=0,pre=0;
for(auto& x:nums)
{
pre+=x;
if(mp.find(pre-k)!=mp.end())count+=mp[pre-k];
mp[pre]++;
}
return count;
}
};
滑动窗口最大值
做法一,优先队列存储,加上一个存储下标的操作即可,用pair。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n=nums.size();
priority_queue<pair<int,int>> q;
for(int i=0;i<k;i++)q.emplace(nums[i],i);
vector<int> ans={q.top().first};
for(int i=k;i<n;i++)
{
q.emplace(nums[i],i);
while(q.top().second<=i-k)q.pop();
ans.emplace_back(q.top().first);
}
return ans;
}
};
做法二 其实感觉以前写过类似的。开一个队列进行维护,由于前后都需要操作,所以开deque。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n=nums.size();
deque<int> q;
for(int i=0;i<k;i++)
{
while(!q.empty()&&nums[i]>=nums[q.back()])q.pop_back();
q.emplace_back(i);
}
vector<int> ans={nums[q.front()]};
for(int i=k;i<n;i++)
{
while(!q.empty()&&nums[i]>=nums[q.back()])q.pop_back();
q.emplace_back(i);
while(q.front()<=i-k)q.pop_front();
ans.emplace_back(nums[q.front()]);
}
return ans;
}
};
加油吧,还是刷的题不够多,思考也不够多,三月份其实已经尽我挺大程度的努力了。四月份继续加油,也没有几个月夏令营了 fighting~