力扣Hot100题单个人计划c++版(一)
力扣Hot100题单个人计划c++版(二)
力扣Hot100题单个人计划c++版(三)
力扣Hot100题单个人计划c++版(四)
力扣Hot100题单个人计划c++版(五)
刷题链接:力扣Hot 100
每日一题,每日一更,白板手写。
9.21打卡
数组元素不重复,且都在-10~10之间。简单递归即可。
class Solution {
public:
vector<vector<int>> ans;
int vis[25]={0};
const int k=11;
void dfs(const vector<int>& nums,vector<int>& cur,int idx,int n){
if(idx==n){
ans.emplace_back(cur);
return;
}
for(int i=0;i<n;i++){
if(!vis[nums[i]+k]){
vis[nums[i]+k]=1;
cur.push_back(nums[i]);
dfs(nums,cur,idx+1,n);
cur.pop_back();
vis[nums[i]+k]=0;
}
}
}
vector<vector<int>> permute(vector<int>& nums) {
int n=nums.size();
vector<int> tmp;
dfs(nums,tmp,0,n);
return ans;
}
};
9.22打卡
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n=matrix.size();
for(int i=0;i<n/2;++i){
for(int j=0;j<(n+1)/2;++j){
int tmp=matrix[i][j];
matrix[i][j]=matrix[n-j-1][i];
matrix[n-j-1][i]=matrix[n-i-1][n-j-1];
matrix[n-i-1][n-j-1]=matrix[j][n-i-1];
matrix[j][n-i-1]=tmp;
}
}
}
};
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n=matrix.size();
for(int i=0;i<n/2;++i)
for(int j=0;j<n;++j)
swap(matrix[i][j],matrix[n-i-1][j]);
for(int i=0;i<n-1;++i)
for(int j=i+1;j<n;++j)
swap(matrix[i][j],matrix[j][i]);
}
};
9.23打卡
题目第一眼想到状态压缩,然后发现可以重复字符,状压空间太大。那只能是哈希表写,根据26个字符个数也可以自己设置hash算法。另有一个题解是排序字符,尽管很麻烦但也不失为一种思路,stl模版确实方便,可以用string做key值。
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string,vector<string>> mp;
for(auto str:strs){
string tmp=str;
sort(tmp.begin(),tmp.end());
mp[tmp].emplace_back(str);
}
vector<vector<string>> ans;
for(auto it=mp.begin();it!=mp.end();++it)ans.emplace_back(it->second);
return ans;
}
};
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string,vector<string>> mp;
for(auto str:strs){
int count[26]={0};
for(int i=0;i<str.size();++i)count[str[i]-'a']++;
string key;
for(int i=0;i<26;i++)
if(count[i]){
key.push_back(char('0'+count[i]));
key.push_back(char('a'+i));
}
mp[key].emplace_back(str);
}
vector<vector<string>> ans;
for(auto it=mp.begin();it!=mp.end();++it)
ans.emplace_back(it->second);
return ans;
}
};
9.24打卡
不得不说easy题的题解总是出人意料啊,还真是easy啊。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int ans=nums[0];
int pre=(ans>0?ans:0);
int n=nums.size();
for(int i=1;i<n;i++){
pre+=nums[i];
ans=max(ans,pre);
if(pre<0)pre=0;
}
return ans;
}
};
class Solution {
public:
struct interval{
int isum,lsum,rsum,msum;
};
interval pushup(interval a,interval b){
int isum=a.isum+b.isum;
int lsum=max(a.lsum,a.isum+b.lsum);
int rsum=max(b.rsum,a.rsum+b.isum);
int msum=max(max(a.msum,b.msum),a.rsum+b.lsum);
return (interval){isum,lsum,rsum,msum};
}
interval merge(vector<int>&nums,int l,int r){
if(l==r)return (interval){nums[l],nums[l],nums[l],nums[l]};
int m=l+((r-l)>>1);
interval a=merge(nums,l,m);
interval b=merge(nums,m+1,r);
return pushup(a,b);
}
int maxSubArray(vector<int>& nums) {
return merge(nums,0,nums.size()-1).msum;
}
};
9.25打卡
有点类似最短路径算法Dijsktra算法。两者都是基于贪心。该题思路是每选取一个点,扩展其最远能到达的位置即可。如果该点超出当前能扩展的范围外就返回false,如果能到最后一个点,就返回true。
class Solution {
public:
bool canJump(vector<int>& nums) {
int n=nums.size();
int dj=0;
for(int i=0;i<n;i++){
if(i>dj)return false;
dj=max(dj,nums[i]+i);
}
return true;
}
};
9.26打卡
区间问题,以为是线段树或者贪心。题解有点意外,排序后,从第一个开始不断合并即可。
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if(intervals.size()==1)return intervals;
sort(intervals.begin(),intervals.end());
vector<vector<int>> merge;
for(int i=0;i<intervals.size();i++){
int vl=intervals[i][0],vr=intervals[i][1];
if(i==0||merge.back()[1]<vl)
merge.push_back({vl,vr});
else
merge.back()[1]=max(merge.back()[1],vr);
}
return merge;
}
};
9.27打卡
题目很经典了,经典dp,当然组合数学好的话可以直接推公式出来。
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> dp(m+1,vector<int>(n+1,0));
dp[0][1]=1;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
dp[i][j]=dp[i-1][j]+dp[i][j-1];
return dp[m][n];
}
};
9.28打卡
简单dp,首先初始化第一列和第一行的值,这两个是没法变的,然后从 g r i d [ 1 ] [ 1 ] grid[1][1] grid[1][1]开始选择上方和右边最小的一个。
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int n=grid.size(),m=grid[0].size();
for(int i=1;i<m;i++)grid[0][i]+=grid[0][i-1];
for(int i=1;i<n;i++)grid[i][0]+=grid[i-1][0];
for(int i=1;i<n;i++){
for(int j=1;j<m;j++){
grid[i][j]+=min(grid[i-1][j],grid[i][j-1]);
}
}
return grid[n-1][m-1];
}
};
9.29打卡
经典斐波那契题了。
class Solution {
public:
int climbStairs(int n) {
int a=0,b=0,c=1;
while(n--){
a=b;
b=c;
c=a+b;
}
return c;
}
};
class Solution {
public:
vector<vector<long long>> multiply(vector<vector<long long>>& a,vector<vector<long long>>& b){
vector<vector<long long>> c(2, vector<long long>(2));
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
c[i][j]=a[i][0]*b[0][j]+a[i][1]*b[1][j];
return c;
}
vector<vector<long long>> mulpow(vector<vector<long long>> a,int n){
vector<vector<long long>> ret={{1,0},{0,1}};
while(n){
if(n&1)ret=multiply(ret,a);
n>>=1;
a=multiply(a,a);
}
return ret;
}
int climbStairs(int n) {
vector<vector<long long>> a={{1,1},{1,0}};
vector<vector<long long>> ans=mulpow(a,n);
return ans[0][0];
}
};
9.30打卡
字符串的经典dp了,老题。结果改了改三个操作还是不会。参考官方题解。
class Solution {
public:
int minDistance(string word1, string word2) {
int n=word1.size();
int m=word2.size();
if(!n*m)return n+m;
int dp[n+1][m+1];
for(int i=0;i<=n;i++)dp[i][0]=i;
for(int i=0;i<=m;i++)dp[0][i]=i;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int a=dp[i-1][j]+1;
int b=dp[i][j-1]+1;
int c=dp[i-1][j-1]+int(word1[i-1]!=word2[j-1]);
dp[i][j]=min(a,min(b,c));
}
}
return dp[n][m];
}
};
10.1打卡
要求:仅使用常数空间的一趟扫描算法,且为原地排序(否则直接计算0,1,2的个数即可)。显然双指针算法,一个指向前面换遍历到的0,一个指向后面换遍历到的2。题解的另一种算法大同小异,即一个指向末尾0的位置,一个指向末尾1的位置。
class Solution {
public:
void sortColors(vector<int>& nums) {
int n=nums.size();
int p0=0,p2=n-1;
for(int i=0;i<n;++i){
while(nums[i]==2&&i<p2)swap(nums[i],nums[p2--]);
if(nums[i]==0)swap(nums[i],nums[p0++]);
}
}
};
10.2打卡
不需要顺序一致,明显是滑动窗口,统计窗口内的字符和目标字符个数能不能对上即可。
class Solution {
public:
unordered_map<char,int> ms,mt;
bool check(){
for(const auto &p:mt)
if(ms[p.first]<p.second)return false;
return true;
}
string minWindow(string s, string t) {
for(auto &c:t)++mt[c];
int l,r;
l=r=0;
int n=s.size();
int minlen=n+1,pos=0;
while(r<n){
if(mt.count(s[r])){
++ms[s[r]];
}
while(check()&&l<=r){
if(r-l+1<minlen){
minlen=r-l+1;
pos=l;
}
if(mt.count(s[l]))--ms[s[l]];
++l;
}
++r;
}
return minlen==(n+1)?string():s.substr(pos,minlen);
}
};
10.3打卡
由于元素不相同,其实就是01背包,对于一个元素只有选和不选,遍历所有情况即可。可以采用状压法, ( 0 , 2 n − 1 ) (0,2^{n}-1) (0,2n−1)对应了每一种情况,也可以选择回溯法,对每一个数字选和不选即可。
1.状压法
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
int n=nums.size();
vector<vector<int>> ans;
for(int i=0;i<(1<<n);++i){
vector<int> t;
for(int j=0;j<n;j++){
if(i&(1<<j))t.emplace_back(nums[j]);
}
ans.emplace_back(t);
}
return ans;
}
};
2.回溯法
class Solution {
public:
vector<vector<int>> ans;
vector<int> t;
void dfs(int cur,const int n,vector<int>& nums){
if(cur==n){
ans.emplace_back(t);
return;
}
t.emplace_back(nums[cur]);
dfs(cur+1,n,nums);
t.pop_back();
dfs(cur+1,n,nums);
}
vector<vector<int>> subsets(vector<int>& nums) {
int n=nums.size();
dfs(0,n,nums);
return ans;
}
};
3.还有一个独特的思路,初始化一个空的解集,对于每个数字,遍历解集,每次加入该数字,也能生成解集。其实也可以理解为,所有解集的每个位置的0,都变成一次1,就能得到全部解集。
class Solution {
public:
vector<vector<int>> ans;
vector<vector<int>> subsets(vector<int>& nums) {
int n=nums.size();
vector<int> t;
ans.emplace_back(t);
for(int i=0;i<n;++i){
int m=ans.size();
for(int j=0;j<m;++j){
t=ans[j];
t.push_back(nums[i]);
ans.emplace_back(t);
}
}
return ans;
}
};
10.5打卡。简单的搜索。
class Solution {
public:
vector<pair<int, int>> directions{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
bool dfs(vector<vector<char>>& board,int cur,int curx,int cury,const int n,string word){
if(cur==n){
return true;
}
bool res=false;
for(auto& dir:directions){
int nextx=curx+dir.first,nexty=cury+dir.second;
if(nextx<0||nextx>=board.size()||nexty<0|nexty>=board[0].size())continue;
if(board[nextx][nexty]==word[cur]){
char c=board[nextx][nexty];
board[nextx][nexty]=0;
if(dfs(board,cur+1,nextx,nexty,n,word)){
res=true;
break;
}
board[nextx][nexty]=c;
}
}
return res;
}
bool exist(vector<vector<char>>& board, string word) {
int n=board.size();
int m=board[0].size();
int len=word.size();
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
if(board[i][j]==word[0]){
char c=board[i][j];
board[i][j]=0;
if(dfs(board,1,i,j,len,word))return true;;
board[i][j]=c;
}
}
}
return false;
}
};
10.5打卡
题解是单调栈,更容易理解一些,但本人训练acm时期恰好做过原题,hdoj1506-Largest Rectangle in a Historgram。本题算是非常经典的dp题,该题上一道hdoj1505是本题扩展到二维的题目。本题实际上是对于每一个高度,快速找到左右边界。这样我们可以用dp数组来记录该位置上一个边界地址,每次直接比较原高度和相邻边界,即可找到最终位置。该代码相较官方题解更难理解,但更加快捷。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int n=heights.size();
vector<int> dpl(n);
vector<int> dpr(n);
dpl[0]=0;dpr[n-1]=n-1;
for(int i=1;i<n;++i){
int t=i;
while(t>0&&heights[i]<=heights[t-1])
t=dpl[t-1];
dpl[i]=t;
}
for(int i=n-2;i>=0;--i){
int t=i;
while(t<(n-1)&&heights[i]<heights[t+1])
t=dpr[t+1];
dpr[i]=t;
}
int ans=0;
for(int i=0;i<n;i++){
int tmp=heights[i]*(dpr[i]-dpl[i]+1);
ans=max(ans,tmp);
}
return ans;
}
};
10.6打卡
实际上为hdoj1505,即上一题的二维形式。将每一层加到下一层,对于每一行来说就形成了上一题的题目,选出所有行中最大的即可。这次代码用到了上一题中官方题解的单调栈。实际上换成35题代码也可。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int n = heights.size();
vector<int> left(n), right(n, n);
stack<int> mono_stack;
for (int i = 0; i < n; ++i) {
while (!mono_stack.empty() && heights[mono_stack.top()] >= heights[i]) {
right[mono_stack.top()] = i;
mono_stack.pop();
}
left[i] = (mono_stack.empty() ? -1 : mono_stack.top());
mono_stack.push(i);
}
int ans = 0;
for (int i = 0; i < n; ++i) {
//cout<<"left: "<
ans = max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;
}
int maximalRectangle(vector<vector<char>>& matrix) {
int n=matrix.size();
if(n==0)return 0;
int m=matrix[0].size();
vector<vector<int>> mp(n,vector<int>(m,0));
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
if(matrix[i][j]=='1')mp[i][j]=(i==0?0:mp[i-1][j])+1;
}
}
int ans=0;
for(int i=0;i<n;++i)
ans=max(ans,largestRectangleArea(mp[i]));
return ans;
}
};
10.7打卡
可以递归的话是简单题,中序遍历,函数递归可以很好解决遍历思路。
class Solution {
public:
void inparse(TreeNode* pos,vector<int>& t){
if(pos==nullptr)return;
inparse(pos->left,t);
t.emplace_back(pos->val);
inparse(pos->right,t);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ans;
inparse(root,ans);
return ans;
}
};
再写个非递归版本的吧。题解的morris写法理解就行了,个人感觉写这个真就没必要了。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ans;
stack<TreeNode*> st;
TreeNode* p=root;
while(p!=nullptr||!st.empty()){
while(p!=nullptr){
st.push(p);
p=p->left;
}
p=st.top();st.pop();
ans.emplace_back(p->val);
p=p->right;
}
return ans;
}
};
10.9打卡
组合数学的卡特兰数,只能说数学题没见过根本想不出。
class Solution {
public:
int numTrees(int n) {
vector<int> g(n+1,0);
g[0]=1;
g[1]=1;
for(int i=2;i<=n;++i){
for(int j=1;j<=i;++j){
g[i]+=g[j-1]*g[i-j];
}
}
return g[n];
}
};
10.9打卡
递归检查是否在区间内即可。
class Solution {
public:
bool check(TreeNode* pos,long long l,long long r){
if(pos==nullptr)return true;
if(pos->val <= l||pos->val >= r)return false;
return check(pos->left,l,pos->val)&&check(pos->right,pos->val,r);
}
bool isValidBST(TreeNode* root) {
return check(root,LONG_MIN,LONG_MAX);
}
};
另一种解法是求出其中序遍历的数组,检查其是否升序即可。这是数据结构的知识,不多做说明,利用前面中序遍历的迭代写法可以写出。
10.10打卡
递归写法很简单,每次判断对称两边的值是否相等,然后继续判断左子树的左右节点和右子树的右左节点是否一致。迭代写法即用队列存储每层,判断第一个和最后一个是否一致即可。此处只给出递归版本。
class Solution {
public:
bool check(TreeNode* l,TreeNode* r){
if(!l&&!r)return true;
if(!l||!r)return false;
if(l->val==r->val)
return check(l->left,r->right)&&check(l->right,r->left);
return false;
}
bool isSymmetric(TreeNode* root) {
if(root==nullptr)return true;
return check(root->left,root->right);
}
};