力扣刷题记录

目录

该文章刷题顺序按照代码随想录刷,只记录简单算法思路和源码

数组

链表

哈希表

字符串

双指针法

栈和队列

二叉树

回溯算法

在这里插入图片描述
回溯其实可以说是我们熟悉的DFS,本质上一种暴力枚举算法。回溯算法的唯一优化方法是剪枝。
回溯算法的模板

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

77、组合(中等)

class Solution {
public:
    void backTracking(vector<vector<int>>& res,vector<int>& temp,int n,int k,int index){
        //回溯
        if(temp.size()==k) {
            res.push_back(temp);
            return;
        }
        for(int i=index;i<=n-(k-temp.size())+1;i++){    //剪枝优化
            temp.push_back(i);
            backTracking(res,temp,n,k,i+1); //注意:i+1而不是index+1
            temp.pop_back();
        }
    }
    vector<vector<int>> combine(int n, int k) {
        //回溯的经典算法
        vector<vector<int>> res;
        vector<int> temp;
        backTracking(res,temp,n,k,1);
        return res;
    }
};

216、组合总和3

class Solution {
public:
    void backTracking(vector<vector<int>>& res,vector<int>& temp,int k,int n,int index){
        //回溯
        if(temp.size()==k && n==0){
            res.push_back(temp);
            return;
        }
        for(int i=index;i<=9;i++){ 
            if(n<0) continue;   //剪枝优化
            temp.push_back(i);
            n=n-i;
            backTracking(res,temp,k,n,i+1);
            temp.pop_back();
            n=n+i;
        }

    }
    vector<vector<int>> combinationSum3(int k, int n) {
        /*回溯*/
        vector<vector<int>> res;
        vector<int> temp;
        backTracking(res,temp,k,n,1);
        return res;
    }
};

17、电话号码的字母组合

class Solution {
public:
    void backTracking(vector<string>& res,vector<string>& temp,string& ans,int n,int index){
        //回溯
        if(index==n) {
            res.push_back(ans);
            return;
        }
        for(int i=0;i<temp[index].size();i++){
            ans.push_back(temp[index][i]);
            backTracking(res,temp,ans,n,index+1);
            ans.pop_back();
        }
        return;
    }
    vector<string> letterCombinations(string digits) {
        /**/
        vector<string> temp;
        vector<string> res;
        int n = digits.size();
        if(n==0) return res;
        for (char i : digits){
            if(i=='2') temp.push_back("abc");
            if(i=='3') temp.push_back("def");
            if(i=='4') temp.push_back("ghi");
            if(i=='5') temp.push_back("jkl");
            if(i=='6') temp.push_back("mno");
            if(i=='7') temp.push_back("pqrs");
            if(i=='8') temp.push_back("tuv");
            if(i=='9') temp.push_back("wxyz");
        }
        string ans;
        backTracking(res,temp,ans,n,0);
        return res;
    }
};

39组合总和

class Solution {
public:
    vector<vector<int>> res;
    vector<int> temp;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        /*回溯实现*/
        backTracking(candidates,target,0);
        return res;
    }
    void backTracking(vector<int>& candidates, int target, int index) {
        /*回溯实现*/
        if( target ==0 ){
            res.push_back(temp);
            return ;
        }
        if(target < 0) {
            return ;
        }
        for(int i=index;i<candidates.size();i++){
            if(candidates[i]>target) continue; //剪枝
            temp.push_back(candidates[i]);
            backTracking(candidates,target-candidates[i],i);  //微调,这里是i而不是i+1,因为可以重复使用,保证本层使用了i之后下一层也可以使用i
            temp.pop_back();
        }
        return ;
    }
};

40、组合总和2(该题很好地讲解了去重的原理,多看)

class Solution {
public:
    void backTracking(vector<vector<int>>& res,vector<int>& candidates,vector<int>& path,int target,int index,vector<bool>& used){
        //回溯
        if(candidates.size()==0 || target==0){
            res.push_back(path);
            return;
        }
        for(int i=index;i<candidates.size();i++){
            if(i>0 && candidates[i]==candidates[i-1] && used[i-1]==false) continue; //细节,used[i-1]==false,如果为true,则为树枝上的去重
            used[i] = true;
            path.push_back(candidates[i]);
            backTracking(res,candidates,path,target-candidates[i],i+1,used);
            used[i] = false;
            path.pop_back();
        }
        return;
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        /*典型回溯算法*/
        vector<vector<int>> res;
        vector<int> path;
        vector<bool> used(candidates.size(),false);
        sort(candidates.begin(),candidates.end());
        backTracking(res,candidates,path,target,0,used);
        return res;
    }
};

131、分割回文串(多看,对于理解index和i的关系有好处)

class Solution {
public:
    bool isOrNo(string s){
        //该函数用于判断该子串是否是回文串
        int n=s.size();
        for(int i=0;i<n/2;i++){
            if(s[i]!=s[n-1-i]) return false;
        }
        return true;
    }
    void backTracking(vector<vector<string>>& res,string& s,vector<string>& path,int index){
        /*回溯*/
        if( index ==s.size()){
            res.push_back(path);
            return;
        }
        for(int i=index;i<s.size();i++){
            string s1;
            for(int j=index;j<=i;j++){  //想清楚为什么是从index到i
                s1.push_back(s[j]);
            }
            if(isOrNo(s1)){
                path.push_back(s1);
                backTracking(res,s,path,i+1);
                path.pop_back();
            }else{
                continue;
            }
        }
        return;
    }
    vector<vector<string>> partition(string s) {
        /*回溯算法,分割*/
        vector<vector<string>> res;
        vector<string> path;
        backTracking(res,s,path,0);
        return res;
    }
};

93、复原IP地址

class Solution {
public:
    int cnt=0;
    bool isIP(const string path){
        //该函数用于判断字符是否为0-255之间
        int n=path.size();
        if(n>3) return false;
        if(path[0]=='0' && n!=1) return false;
        if(n==3){
            int val = 0;
            for(int i=0;i<3;i++){
                val=val*10+path[i]-'0';
            }
            if(val>255) return false;
            else return true;
        }
        return true;
    }
    void backTracking(vector<string>& res,const string s,string& path,int index){
        //回溯
        if(cnt==4 && index==s.size()){
            res.push_back(path);
            return;
        }
        for(int i=index;i<s.size();i++){
            string temp;
            if(cnt<=2){
                for(int j=index;j<=i;j++){
                temp.push_back(s[j]);
                }
            }else{
                for(int j=index;j<s.size();j++){
                temp.push_back(s[j]);
                }
            }
            if(isIP(temp)){
                int sz=path.size();
                if(cnt==0) path+=temp;
                else{
                    path=path+'.'+temp;
                }
                cnt++;
                backTracking(res,s,path,i+1);
                path.resize(sz);
                cnt--;
            }else{
                break;
            }
        }
        return;
    }
    vector<string> restoreIpAddresses(string s) {
        /*分割字符串问题
        */
        vector<string> res;
        string path;
        backTracking(res,s,path,0);
        return res;
    }
};

78、子集

class Solution {
public:
    void backTracking(vector<vector<int>>& res,vector<int>& path,const vector<int> nums,int index){
        //回溯
        res.push_back(path);
        if(index==nums.size()){
            return;
        }
        for(int i=index;i<nums.size();i++){
            path.push_back(nums[i]);
            backTracking(res,path,nums,i+1);
            path.pop_back();
        }
        return;
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> res;
        vector<int> path;
        backTracking(res,path,nums,0);
        return res;
    }
};

贪心算法

贪心的本质是选择每一阶段的局部最优,从而达到全局最优。
贪心算法一般分为如下四步:
将问题分解为若干个子问题
找出适合的贪心策略
求解每一个子问题的最优解
将局部最优解堆叠成全局最优解

455、分发饼干

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        /*将胃口值g进行排序,优先满足小胃口的*/
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int n=g.size(),m=s.size();
        int cnt =0;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(g[i]<=s[j]){
                    cnt++;
                    s[j]=0;
                    break;
                }
            }
        }
        return cnt;
    }
};

376、摆动序列

动态规划

动规五部曲:
确定dp数组(dp table)以及下标的含义
确定递推公式
dp数组如何初始化
确定遍历顺序
举例推导dp数组

509、斐波那契数

class Solution {
public:
    int fib(int n) {
        /*动态规划问题*/
        if(n==0 ) return 0;
        if(n==2|| n==1) return 1;
        vector<int> DP;
        DP.push_back(0);
        DP.push_back(1);
        int i=2;
        for(i;i<=n;i++){
            DP.push_back(DP[i-2]+DP[i-1]);
        }
        return DP[n];
    }
};

70、爬楼梯

class Solution {
public:
    int climbStairs(int n) {
        /*动态规划
        递推公式f(n)=f(n-1)+f(n-2)
        DP数组表示爬i阶有DP[i]种方法
        初始化DP(0)=1,DP(1)=1,DP(2)=2
        从前向后遍历*/
        vector<int> DP(n+1);
        DP[0]=1;DP[1]=1;
        for(int i=2;i<=n;i++){
            DP[i] = DP[i-1]+DP[i-2];
        }
        return DP[n];
    }
};

可以优化DP数组,只存储两个值即可

class Solution {
public:
    int climbStairs(int n) {
        /*动态规划
        递推公式f(n)=f(n-1)+f(n-2)
        DP数组表示爬i阶有DP[i]种方法
        初始化DP(0)=1,DP(1)=1,DP(2)=2
        从前向后遍历*/
        vector<int> DP(2);
        DP[0]=1;DP[1]=1;
        for(int i=2;i<=n;i++){
            int temp = DP[0]+DP[1];
            DP[0] = DP[1];
            DP[1] = temp;
        }
        return DP[1];
    }
};

746、使用最小花费爬楼梯

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        /*动态规划
        DP[i]表示当前在第i个台阶,下一步累积的最低花费
        初始化:DP[0]=0,DP[1]=0
        递推公式:f(n)=min(f(n-1)+cost(n-1),f(n-2)+cost(n-2))
        */
        int n=cost.size();
        if(n==0) return 0;
        vector<int> DP(n+1);
        DP[0]=0;DP[1]=0;
        for(int i=2;i<=n;i++){
            DP[i] = min(DP[i-1]+cost[i-1],DP[i-2]+cost[i-2]);
        }
        return DP[n];
    }
};

62、不同路径

class Solution {
public:
    int uniquePaths(int m, int n) {
        /*动态规划
        DP[i][j]表示走到第i,j位置总共有的路径条数
        初始化DP[0][j]=1,DP[i][0]=1;
        递推公式:DP[i][j]=DP[i-1][j]+DP[i][j-1],注意边界条件
        遍历顺序:*/
        vector<vector<int>> A;
        for(int i=0;i<m;i++){
            vector<int> temp(n,1);
            A.push_back(temp);
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                A[i][j]=A[i-1][j]+A[i][j-1];
            }
        }
        return A[m-1][n-1];
    }
};

63、不同路径2

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        /*动态规划:
        DP[i][j]代表走到第i,j位置需要的路径数,如果有障碍则置为0
        DP[i][0]=1,DP[0][j]=1,如果边界上有障碍,则后续为0
        递推关系:分四种情况,没障碍,障碍在上面,障碍在左边,两边都有障碍
        遍历顺序:*/
        vector<vector<int>> DP;
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        //DP初始化
        for(int i=0;i<m;i++){
            vector<int> temp(n,0);
            DP.push_back(temp);
        }
        for(int i=0;i<n;i++){
            if(obstacleGrid[0][i]==1) break;
            DP[0][i]=1;
        }
        for(int i=0;i<m;i++){
            if(obstacleGrid[i][0]==1) break;
            DP[i][0]=1;
        }
        //遍历DP数组
        for(int j=1;j<n;j++){
            for(int i=1;i<m;i++){
                if(obstacleGrid[i-1][j]!=1 && obstacleGrid[i][j-1]!=1)  {
                    DP[i][j]=DP[i-1][j]+DP[i][j-1];
                }
                else if(obstacleGrid[i-1][j]==1 && obstacleGrid[i][j-1]!=1){
                    DP[i][j]=DP[i][j-1];
                }
                else if(obstacleGrid[i][j-1]==1 && obstacleGrid[i-1][j]!=1){
                    DP[i][j]=DP[i-1][j];
                }
                else{
                    DP[i][j]=0;
                }
            }
        }
        if(obstacleGrid[m-1][n-1]==1)   return 0;
        else    return DP[m-1][n-1];
    }
};

343、整数拆分

该题难在递推关系,怎么推导。

class Solution {
public:
    int integerBreak(int n) {
        /*DP[i]表示拆分i,返回的乘积最大值
        初始值:DP[1]=1,DP[2]=1,DP[3] =2
        递推关系:DP[i] = max(j*DP[i-j],j*(i-j)),j从1到i
        遍历顺序:依次*/
        vector<int> DP(n+1,1);
        DP[1]=1; DP[2]=1;
        for(int i=3;i<n+1;i++){
            for(int j=1;j<i;j++){
                int temp = DP[i];
                DP[i] = max(j*(i-j),j*DP[i-j]);
                DP[i] = max(DP[i],temp);
            }
        }
        return DP[n];
    }
};

96、不同的二叉搜索树

关键在于递推公式为:
D P [ i ] = ∑ j = 0 i − 1 D P [ j ] D P [ i − j − 1 ] DP[i]=\sum_{j=0}^{i-1} DP[j]DP[i-j-1] DP[i]=j=0i1DP[j]DP[ij1]

class Solution {
public:
    int numTrees(int n) {
        /*动态规划,难点在于递推公式*/
        vector<int> DP(n+1,0);
        DP[0]=1;DP[1]=1;
        for(int i=2;i<=n;i++){
            for(int j=0;j<i;j++){
                DP[i] += DP[j]*DP[i-j-1];
            }
        }
        return DP[n];
    }
};

动规:背包问题

在这里插入图片描述
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
在这里插入图片描述

01背包问题

每个物品只有一个,求背包能容纳的最大价值。(看代码随想录讲解,很详细)
两个关注点:1、递推公式的推导,2、两个for循环遍历的顺序(如果求组合数,外层for循环遍历物品,内层for遍历背包,如果求排列数,外层for遍历背包,内层for遍历物品,01背包内层for循环逆序遍历,完全背包内层for循环顺序遍历)。

注意:怎么理解遍历背包时,逆序是01背包,顺序是完全背包。因为,DP数组本身每一项DP[i]都是依赖于DP[0~i-1]前i-1项推导出来的,如果顺序则会出现套娃的情况,实现的效果为每个物品任取背包容量大小次。而逆序,先更新第i+1,后更新第i项,更新结果取决于初始值,则每个物品只取了一次。

416、分割等和子集

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        /*每个数取一次,看成动态规划里的01背包问题,本题物品价值等于物品大小
        DP[j]表示,背包大小为j的背包,所能装载的最大价值
        初始化:DP[0]=0,DP[j]=0
        递推公式:DP[j]=max{DP[j],DP[j-nums[i]]+nums[i]}
        遍历顺序:先物品i,再背包大小j(逆序)
        打印DP数组*/
        int sum=0;
        int n=nums.size();
        for(int i=0;i<n;i++){
            sum+=nums[i];
        }
        int target = sum/2;
        if(sum%2==1) return false;
        vector<int> DP(target+1,0);
        for(int i=0;i<n;i++){
            for(int j=target;j>=nums[i];j--){
                DP[j] = max(DP[j],DP[j-nums[i]]+nums[i]);
            }
        }
        if(DP[target]==target) return true;
        return false;
    }
};

1049、最后一块石头的重量2

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        /*分解为,将石头分为大小和几乎相等的两堆,
        相撞后差的最小值,如果两堆大小相等则返回0*/
        int n=stones.size();
        int sum=0;
        for(int i=0;i<n;i++){
            sum+=stones[i];
        }
        int target = sum/2;
        vector<int> DP(target+1,0);
        for(int i=0;i<n;i++){
            for(int j=target;j>=stones[i];j--){
                DP[j] = max(DP[j],DP[j-stones[i]]+stones[i]);
            }
        }
        return abs(sum-2*DP[target]);

    }
};

494、目标和

DP[j]表示装满大小为j的背包有DP[j]中方法。
递推公式:
D P [ j ] = ∑ i = 0 n − 1 D P [ j − n u m s [ i ] ] DP[j]=\sum_{i=0}^{n-1} DP[j-nums[i]] DP[j]=i=0n1DP[jnums[i]]

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        /*动规五部曲*/
        int sum=0;
        for(int i:nums) sum+=i;
        if(abs(target)>sum) return 0;
        if((target+sum)%2==1) return 0;
        int left = (target+sum)/2;
        vector<int> DP(left+1,0);
        DP[0] =1;
        for(int i=0;i<nums.size();i++){
            for(int j=left;j>=nums[i];j--){
                DP[j]+=DP[j-nums[i]];
            }
        }
        return DP[left];
    }
};

474、一和零

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>> dp(m + 1, vector<int> (n + 1, 0)); // 默认初始化0
        for (string str : strs) { // 遍历物品
            int oneNum = 0, zeroNum = 0;
            for (char c : str) {
                if (c == '0') zeroNum++;
                else oneNum++;
            }
            for (int i = m; i >= zeroNum; i--) { // 遍历背包容量且从后向前遍历!
                for (int j = n; j >= oneNum; j--) {
                    dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
                }
            }
        }
        return dp[m][n];
    }
};

完全背包问题

每个物品可以使用无数次

518、零钱兑换2

递推公式:
D P [ j ] = ∑ i = 0 n − 1 D P [ j − c o i n [ i ] ] DP[j]=\sum_{i=0}^{n-1} DP[j-coin[i]] DP[j]=i=0n1DP[jcoin[i]]

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        /*动规五部曲
        dp[j] 就是所有的dp[j - coins[i]](考虑coins[i]的情况)相加。*/
        int n=coins.size();
        vector<int> DP(amount+1,0);
        DP[0]=1;
        for(int i=0;i<n;i++){
            for(int j=coins[i];j<=amount;j++){
                DP[j]+=DP[j-coins[i]];
            }
        }
        return DP[amount];
    }
};

377、组合总和4

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        /**/
        int n=nums.size();
        vector<int> DP(target+1,0);
        DP[0]=1;
        for (int i = 0; i <= target; i++) { // 遍历背包
            for (int j = 0; j < n; j++) { // 遍历物品
                if (i - nums[j] >= 0 && DP[i] < INT_MAX - DP[i - nums[j]]) {
                    DP[i] += DP[i - nums[j]];
                }
            }
        }
        return DP[target];
    }
};

322、零钱兑换

递推公式:
D P [ j ] = m i n i = 0 n − 1 ( D P [ j ] , D P [ j − c o i n s [ i ] ] + 1 ) DP[j]=min_{i=0}^{n-1}(DP[j],DP[j-coins[i]]+1) DP[j]=mini=0n1(DP[j],DP[jcoins[i]]+1)

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        /*动规五部曲*/
        int n=coins.size();
        vector<int> DP(amount+1,INT_MAX-1);
        DP[0]=0;
        for(int i=0;i<n;i++){
            for(int j=coins[i];j<=amount;j++){
                DP[j]=min(DP[j],DP[j-coins[i]]+1);
            }
        }
        if(DP[amount]==INT_MAX-1) return -1;
        else return DP[amount];
    }
};

279、完全平方数

递推公式:
D P [ j ] = m i n i = 0 n u m s . s i z e ( ) − 1 ( D P [ j ] , D P [ j − n u m s [ i ] ] + 1 ) DP[j]=min_{i=0}^{nums.size()-1}(DP[j],DP[j-nums[i]]+1) DP[j]=mini=0nums.size()1(DP[j],DP[jnums[i]]+1)
遍历顺序:完全背包问题,且求组合,先物品,后背包,背包顺序遍历。

class Solution {
public:
    int numSquares(int n) {
        /*递归五部曲*/
        vector<int> nums;
        for(int i=1;i*i<=n;i++){
            nums.push_back(i*i);
        }
        vector<int> DP(n+1,INT_MAX);
        DP[0]=0;DP[1]=1;
        for(int i=0;i<nums.size();i++){
            for(int j=nums[i];j<=n;j++){
                DP[j]=min(DP[j],DP[j-nums[i]]+1);
            }
        }
        return DP[n];
    }
};

139、单词拆分

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(),wordDict.end());
        int n=s.size();
        vector<bool> DP(n+1,false);
        DP[0] = true;
        for(int j=1;j<=n;j++){    //遍历背包
            for(int i=0;i<j;i++){ //遍历物品
                string temp = s.substr(i,j-i);
                if(wordSet.find(temp)!=wordSet.end() && DP[i])
                    DP[j] = true;
            }
        }
        return DP[n];
    }
};

动规:打家劫舍系列

198、打家劫舍

DP[j] 表示j个房间能偷到的最高金额
递推公式:
D P [ j ] = m a x ( D P [ j − 1 ] , D P [ j − 2 ] + n u m s [ j − 1 ] ) DP[j] = max(DP[j-1],DP[j-2]+nums[j-1]) DP[j]=max(DP[j1],DP[j2]+nums[j1])
初始化:DP[0]=0,DP[1]=nums[0]
遍历顺序:顺序
打印DP数组

class Solution {
public:
    int rob(vector<int>& nums) {
        /*动规五部曲*/
        int n=nums.size();
        if(n==1) return nums[0];
        vector<int> DP(n+1,0);
        DP[1]=nums[0];
        for(int i=2;i<=n;i++){
            DP[i]=max(DP[i-1],DP[i-2]+nums[i-1]);
        }
        return DP[n];
    }
};

213、打家劫舍2

内容同1,将1进行拆分成两个数组。

class Solution {
public:
    int rob_old(vector<int>& nums) {
        /*动规五部曲*/
        int n=nums.size();
        if(n==1) return nums[0];
        vector<int> DP(n+1,0);
        DP[1]=nums[0];
        for(int i=2;i<=n;i++){
            DP[i]=max(DP[i-1],DP[i-2]+nums[i-1]);
        }
        return DP[n];
    }
    int rob(vector<int>& nums) {
        /*将打家劫舍1分装成函数,将该问题拆分为两个子数组,分别传入函数即可*/
        int n = nums.size();
        if(n==1) return nums[0];
        vector<int> nums_pre,nums_sub;
        for(int i=0;i<n;i++){
            if(i==0){
                nums_pre.push_back(nums[i]);
            }else if(i==n-1){
                nums_sub.push_back(nums[i]);
            }else{
                nums_sub.push_back(nums[i]);
                nums_pre.push_back(nums[i]);
            }
        }
        return max(rob_old(nums_pre),rob_old(nums_sub));
    }
};

337、打家劫舍3(没怎么看懂)

动态规划,使用01DP数组

class Solution {
public:
    int rob(TreeNode* root) {
        vector<int> result = robTree(root);
        return max(result[0], result[1]);
    }
    // 长度为2的数组,0:不偷,1:偷
    vector<int> robTree(TreeNode* cur) {
        if (cur == nullptr) return vector<int>{0, 0};
        vector<int> left = robTree(cur->left);
        vector<int> right = robTree(cur->right);
        // 偷cur,那么就不能偷左右节点。
        int val1 = cur->val + left[0] + right[0];
        // 不偷cur,那么可以偷也可以不偷左右节点,则取较大的情况
        int val2 = max(left[0], left[1]) + max(right[0], right[1]);
        return {val2, val1};
    }
};

动规:股票系列

121、买卖股票的最佳时机

DP[i]表示第i天卖出股票所能获得的最大利润为DP[i]
递推公式: D P [ i ] = p r i c e s [ i ] − m i n . p r e DP[i]=prices[i]-min.pre DP[i]=prices[i]min.pre其中min.pre表示第i天及之前所出现的最低价格

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        /*动态规划*/
        int min_pre=prices[0];
        int n = prices.size();
        vector<int> DP(n+1,0);
        int max_val=0;
        for(int i=1;i<n;i++){
            min_pre = min(min_pre,prices[i]);
            DP[i] = prices[i]-min_pre;
            max_val = max(DP[i],max_val);
        }
        return max_val;
    }
};

122、买卖股票的最佳时机2

没使用动态规划,直接做差求和。代码如下

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        /*遍历一遍prices如果前项小于后项,则做差,否则为0*/
        int n=prices.size();
        int sum=0;
        for(int i=1;i<n;i++){
            int k = prices[i]-prices[i-1];
            if(k>0) sum+=k;
        }
        return sum;
    }
};

动规:子序列系列

300、最长递归子序列

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        /*难点在于DP数组的定义,以及递推公式*/
        int n=nums.size();
        vector<int> DP(n,1);
        int max_val=1;
        for(int i=1;i<n;i++){
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]) DP[i] = max(DP[j]+1,DP[i]);
            }
            max_val = max(max_val,DP[i]);
        }
        return max_val;
    }
};

674、最长连续递增序列

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        int n=nums.size();
        vector<int> DP(n,1);
        int max_val = 1;
        for(int i=1;i<n;i++){
            if(nums[i]>nums[i-1]) DP[i]=DP[i-1]+1;
            max_val=max(max_val,DP[i]);
        }
        return max_val;
    }
};

718、最长重复(连续)子数组

class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        /*本题难点在于DP数组的定义
        DP[i][j]定义为以i-1为结尾的nums1,和以j-1为结尾的nums2,最长重复子数组的长度
        递推公式:DP[i][j]=DP[i-1][j-1]+1,if(nums1[i]==nums2[j])*/
        int m=nums1.size();
        int n=nums2.size();
        vector<vector<int>> DP(m+1,vector<int> (n+1,0));
        int max_val=0;
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(nums1[i-1]==nums2[j-1]) DP[i][j]=DP[i-1][j-1]+1;
                max_val=max(max_val,DP[i][j]);
            }
        }
        return max_val;
    }
};

1143、最长公共子序列(不连续)

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        /*子序列,不连续*/
        int m=text1.size();
        int n=text2.size();
        vector<vector<int>> DP(m+1,vector<int> (n+1,0));
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(text1[i-1]==text2[j-1]) DP[i][j] = DP[i-1][j-1]+1;
                else DP[i][j] = max(DP[i][j-1],DP[i-1][j]);
            }
        }
        return DP[m][n];
    }
};

1035、不相交的线

class Solution {
public:
    int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
        /*动态规划
        DP[i][j]表示以i-1为结尾的nums1和以j-1为结尾的nums2的最大连线数
        DP[i][j]=DP[i-1][j-1] if(nums1[i-1]==nums2[j-1])
                =max(DP[i-1][j],DP[i][j-1]) else
        初始化:全部为0
        遍历顺序
        打印DP数组*/
        int n=nums1.size();
        int m=nums2.size();
        vector<vector<int>> DP(n+1,vector<int> (m+1,0));
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(nums1[i-1]==nums2[j-1]) DP[i][j]=DP[i-1][j-1]+1;
                else DP[i][j] = max(DP[i-1][j],DP[i][j-1]);
            }
        }
        return DP[n][m];
    }
};

53、最大子数组和

如果使用双指针,会超时。建议动态规划

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        /*双指针*/
        int n=nums.size();
        if(n==0) return 0;
        int sum=0;
        int max_val=nums[0];
        for(int left=0;left<n;left++){
            for(int right=left;right<n;right++){
                int temp_val=0;
                for(int temp=left;temp<=right;temp++){
                    temp_val+=nums[temp];
                }
                if(temp_val<=0) {
                    max_val=max(max_val,nums[right]);
                    left=right+1;
                    continue;
                }
                max_val=max(max_val,temp_val);
            }
        }
        return max_val;
    }
};

动规:
DP[i]表示以i为结尾的数组nums中最大的子数组和为DP[i]
递推公式
D P [ i ] = m a x ( D P [ i − 1 ] + n u m s [ i ] , n u m s [ i ] ) DP[i]=max(DP[i-1]+nums[i],nums[i]) DP[i]=max(DP[i1]+nums[i],nums[i])

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        /*难点在于递推公式即DP数组的定义*/
        int n=nums.size();
        if(n==0) return 0;
        vector<int> DP(n,0);
        DP[0] = nums[0];
        int max_val=DP[0];
        for(int i=1;i<n;i++){
            DP[i] = max(DP[i-1]+nums[i],nums[i]);
            max_val = max(DP[i],max_val);
        }
        return max_val;
    }
};

392、判断子序列

class Solution {
public:
    bool isSubsequence(string s, string t) {
        //双指针
        int s_size = s.size();
        int t_size = t.size();
        int i=0;
        for(int j=0;j<t_size;j++){
            if(t[j]==s[i]) i++;
        }
        if(i==s_size) return true;
        else return false;
    }
};

115、不同的子序列

uint64_t,注意写法

class Solution {
public:
    int numDistinct(string s, string t) {
        /*难点在于DP数组的定义,以及递推公式,初始化*/
        int n=s.size();
        int m=t.size();
        vector<vector<uint64_t>> DP(n+1,vector<uint64_t> (m+1,0));
        for(int i=0;i<=n;i++)   DP[i][0] = 1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(s[i-1]==t[j-1]) DP[i][j] = DP[i-1][j-1]+DP[i-1][j];
                else DP[i][j] = DP[i-1][j];
            }
        }
        return DP[n][m];
    }
};

单调栈

图论

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于力扣刷C++常用操作,我可以给你一些常见的操作和技巧: 1. 使用 STL 容器和算法库:STL(Standard Template Library)是 C++ 标准库中的一个重要组成部分,包含了许多常用的容器和算法。在力扣刷中,使用 STL 可以大大提高代码的效率和可读性。例如,vector 可以用来存储动态数组,sort 可以用来排序等等。 2. 使用 auto 关键字:auto 关键字可以自动推导变量类型,可以减少代码量和提高可读性。例如,auto x = 1; 可以自动推导出 x 的类型为 int。 3. 使用 lambda 表达式:lambda 表达式是 C++11 中引入的一种匿名函数,可以方便地定义一些简单的函数对象。在力扣刷中,使用 lambda 表达式可以简化代码,例如在 sort 函数中自定义比较函数。 4. 使用位运算:位运算是一种高效的运算方式,在力扣刷中经常会用到。例如,左移运算符 << 可以用来计算 2 的幂次方,右移运算符 >> 可以用来除以 2 等等。 5. 使用递归:递归是一种常见的算法思想,在力扣刷中也经常会用到。例如,二叉树的遍历、链表的反转等等。 6. 使用 STL 中的 priority_queue:priority_queue 是 STL 中的一个容器,可以用来实现堆。在力扣刷中,使用 priority_queue 可以方便地实现一些需要维护最大值或最小值的算法。 7. 使用 STL 中的 unordered_map:unordered_map 是 STL 中的一个容器,可以用来实现哈希表。在力扣刷中,使用 unordered_map 可以方便地实现一些需要快速查找和插入的算法。 8. 使用 STL 中的 string:string 是 STL 中的一个容器,可以用来存储字符串。在力扣刷中,使用 string 可以方便地处理字符串相关的问。 9. 注意边界条件:在力扣刷中,边界条件往往是解决问的关键。需要仔细分析目,考虑各种边界情况,避免出现错误。 10. 注意时间复杂度:在力扣刷中,时间复杂度往往是评判代码优劣的重要指标。需要仔细分析算法的时间复杂度,并尽可能优化代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值