Loading

Loading

基础算法复习

基础算法复习

来源:力扣面试必刷150题

准备转专业面试。

动态规划

简单背包dp

122.买卖股票的最佳时机II

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。然而,你可以在 同一天 多次买卖该股票,但要确保你持有的股票不超过一股。

返回 你能获得的 最大 利润

示例 1:

输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3。
最大总利润为 4 + 3 = 7 。

本题最优解是使用简单二维dp

状态转移:

第一维 i 记录当前位置,第二维记录买入/卖出状态。

\[dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]); \]

\[dp[i][1]=max(dp[i-1][0]-prices[i],dp[i-1][1]); \]

最后返回 \(dp[n-1][0]\) 即为最终答案。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        int dp[100010][2];
        //初始化第一天卖出/买入的情况。
        dp[0][0]=0,dp[0][1]=-prices[0];
        for(int i=1;i<n;i++)
        {
            //在这天卖出
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
            //在这天买入
            dp[i][1]=max(dp[i-1][0]-prices[i],dp[i-1][1]);
        }
        return dp[n-1][0];
    }
};

45.跳跃游戏II

给定一个长度为 n0 索引整数数组 nums。初始位置在下标 0。

每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。换句话说,如果你在索引 i 处,你可以跳转到任意 (i + j) 处:

  • 0 <= j <= nums[i]
  • i + j < n

返回到达 n - 1 的最小跳跃次数。测试用例保证可以到达 n - 1

示例 1:

输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

我的想法很简单:使用dp[i] 来维护到当前位置的最大长度

有:

\[dp[j]=min(dp[i]+1,dp[j]); \]

class Solution {
public:
    int jump(vector<int>& nums) {
        int dp[100010];
        int n=nums.size();
        for(int i=0;i<n;i++)
        {
            dp[i]=0x3f3f3f3f;
        }
        dp[0]=0;
        for(int i=0;i<n;i++)
        {
            for(int j=i+1;nums[i]>0;j++)
            {
                dp[j]=min(dp[j],dp[i]+1);
                nums[i]--;
            }
        }
        return dp[n-1];
    }
};

300.最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4

示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

状态转移:

需要两层循环,若当前nums[i] > nums[j] 则可以继续拼成最长上升子序列

\[f[i]=max(f[i],f[j]+1) \]

初始化,最短长度为1

\[f[i]=1 \]

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n=nums.size();
        int f[100010];
        for(int i=0;i<n;i++)
        {
            f[i]=1;
            for(int j=0;j<=i;j++)
            {
                if(nums[i]>nums[j])
                {
                    f[i]=max(f[i],f[j]+1);
                }
            }
        }
        int maxx=0;
        for(int i=0;i<n;i++)
        {
            maxx=max(maxx,f[i]);
        }
        return maxx;
    }
};

数字三角形

无须多言

\[dp[i][j]=min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j] \]

可以把第一维优化掉。得到下面的代码

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int n = triangle.size();
        vector<int> dp = triangle[n-1]; 
        for (int i = n-2; i >= 0; i--) {
            for (int j = 0; j <= i; j++) {
                dp[j] = min(dp[j], dp[j+1]) + triangle[i][j];
            }
        }
        return dp[0];
    }
};

63.不同路径II

给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角(即 grid[0][0])。机器人尝试移动到 右下角(即 grid[m - 1][n - 1])。机器人每次只能向下或者向右移动一步。

网格中的障碍物和空位置分别用 10 来表示。机器人的移动路径中不能包含 任何 有障碍物的方格。

返回机器人能够到达右下角的不同路径数量。

测试用例保证答案小于等于 2 * 109

示例 1:

img

输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

很容易想到状态转移方程:

\[dp[i][j]=dp[i-1][j]+dp[i][j-1] \]

有障碍物的时候初始化一下即可。

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int n=obstacleGrid.size(),m=obstacleGrid[0].size();
        int f[n][m];
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
                f[i][j]=0;
        for(int i=0;i<n;i++)
        {
            if(obstacleGrid[i][0]==1) break;
            f[i][0]=1;
        }
        for(int j=0;j<m;j++)
        {
            if(obstacleGrid[0][j]==1) break;
            f[0][j]=1;
        }
        for(int i=1;i<n;i++)
        {
            for(int j=1;j<m;j++)
            {
                if(obstacleGrid[i][j]==1) continue;
                if(f[i-1][j]==0)
                {
                    f[i][j]=f[i][j-1];
                }else if(f[i][j-1]==0)
                {
                    f[i][j]=f[i-1][j];
                }else{
                    f[i][j]=f[i-1][j]+f[i][j-1];
                }
                
            }
        }
        return f[n-1][m-1];
    }
};

322.零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3 
解释:11 = 5 + 5 + 1

示例 2:

输入:coins = [2], amount = 3
输出:-1

示例 3:

输入:coins = [1], amount = 0
输出:0

本题为背包dp,状态转移方程如下:

\[f[i]=min(f[i],f[i-coins[j]]+1); \]

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        int n=coins.size();
        int f[10010];
        for(int i=0;i<=amount;i++) f[i]=0x3f3f3f3f;
        f[0]=0;
        int tmp;
        for(int i=1;i<=amount;i++)
        {
            for(int j=0;j<n;j++)
            {   
                if(i-coins[j]>=0)
                    f[i]=min(f[i],f[i-coins[j]]+1);
            }
        }
        if(f[amount]==0x3f3f3f3f) return -1;
        return f[amount];
    }
};

哈希表

太久没写了忘光光了/oh

class RandomizedSet {
public:
    RandomizedSet() {
        srand((unsigned)time(NULL));
    }
    
    bool insert(int val) {
        if (indices.count(val)) {
            return false;
        }
        int index = nums.size();
        nums.emplace_back(val);
        indices[val] = index;
        return true;
    }
    
    bool remove(int val) {
        if (!indices.count(val)) {
            return false;
        }
        int index = indices[val];
        int last = nums.back();
        nums[index] = last;
        indices[last] = index;
        nums.pop_back();
        indices.erase(val);
        return true;
    }
    
    int getRandom() {
        int randomIndex = rand()%nums.size();
        return nums[randomIndex];
    }
private:
    vector<int> nums;
    unordered_map<int, int> indices;
};

前缀/后缀和

42.接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

img

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

不难发现,我们从左右两侧分别统计到当前位置的最大值 maxx1[i]maxx2[i],每个位置的雨水就是

\[min(maxx1[i],maxx2[i]) \]

class Solution {
public:
    int trap(vector<int>& height) {
        int maxx1[20010],maxx2[20010];
        //const int INF=0x3f3f3f3f;
        int n=height.size();
        maxx1[0]=height[0],maxx2[n-1]=height[n-1];
        for(int i=1;i<n;i++)
        {
            maxx1[i]=max(maxx1[i-1],height[i]);
        }
        for(int i=n-2;i>=0;i--)
        {
            maxx2[i]=max(maxx2[i+1],height[i]);
        }
        int tmp=0,tot=0;
        for(int i=1;i<n;i++)
        {
            tmp=min(maxx1[i],maxx2[i]);
            if(height[i]<tmp)
            {
                tot+=tmp-height[i];
            }
        }  
        return tot;         
    }
};
posted @ 2026-06-07 15:56  Miya555  阅读(6)  评论(0)    收藏  举报