动态规划(100道)
分类说明
以下题目均来源于力扣,其汇总主要是按照难易程度进行划分,大家可以选择适合自己的部分进入。
题目难度:容易
- 53.最大子序和 (C++实现)
- 70.爬楼梯(C++实现)
- 118.杨辉三角(C++实现)
- 119.杨辉三角 Ⅱ(C++实现)
- 121.买卖股票的最佳时机(C++实现)
- 338.比特位计数(C++实现)
- 392.判断子序列(C++实现)
- 509.斐波那契数列(C++实现)
题目难度:中等
- 230. 二叉搜索树中第K小的元素(C++实现)
- 5. 最长回文子串
- 22. 括号生成(C++实现)
- 45. 跳跃游戏 II
- 55. 跳跃游戏
- 62. 不同路径(C++实现)
- 63. 不同路径 II(C++实现)
- 64. 最小路径和(C++实现)
- 91. 解码方法(C++实现)
- 95. 不同的二叉搜索树 II
- 96. 不同的二叉搜索树
- 97. 交错字符串
- 120. 三角形最小路径和
- 122. 买卖股票的最佳时机 II(C++实现)
- 131. 分割回文串
- 139. 单词拆分
- 152. 乘积最大子数组
- 198. 打家劫舍
- 213. 打家劫舍 II
- 221. 最大正方形
- 241. 为运算表达式设计优先级
- 264. 丑数 II
- 279. 完全平方数
- 300. 最长递增子序列
- 309. 最佳买卖股票时机含冷冻期
- 313. 超级丑数
- 322. 零钱兑换
- 337. 打家劫舍 III
- 343. 整数拆分
- 357. 计算各个位数不同的数字个数
- 368. 最大整除子集
- 375. 猜数字大小 II
- 376. 摆动序列
- 377. 组合总和 Ⅳ
- 396. 旋转函数
- 397. 整数替换
- 413. 等差数列划分
- 416. 分割等和子集
- 435. 无重叠区间
- 464. 我能赢吗
- 467. 环绕字符串中唯一的子字符串
- 473. 火柴拼正方形
- 474. 一和零
- 486. 预测赢家
- 494. 目标和
- 516. 最长回文子序列
- 518. 零钱兑换 II
- 526. 优美的排列
- 542. 01 矩阵
- 553. 最优除法
- 576.出界的路径数
- 583. 两个字符串的删除操作
- 638. 大礼包
- 647. 回文子串
- 650. 只有两个键的键盘
- 673. 最长递增子序列的个数
- 678. 有效的括号字符串
- 688. “马”在棋盘上的概率
- 剑指offer 14 剪绳子((C++实现)
题目难度:困难
- 10. 正则表达式匹配
- 32. 最长有效括号
- 42. 接雨水
- 44. 通配符匹配
- 72. 编辑距离
- 85. 最大矩形
- 87. 扰乱字符串
- 115. 不同的子序列
- 123. 买卖股票的最佳时机 III
- 124. 二叉树中的最大路径和
- 132. 分割回文串 II
- 140. 单词拆分 II
- 174. 地下城游戏
- 188. 买卖股票的最佳时机 IV
- 数字 1 的个数
- 312. 戳气球
- 329. 矩阵中的最长递增路径
- 354. 俄罗斯套娃信封问题
- 363. 矩形区域不超过 K 的最大数值和
- 403. 青蛙过河
- 410. 分割数组的最大值
- 446. 等差数列划分 II - 子序列
- 458. 可怜的小猪
- 466. 统计重复个数
- 472. 连接词
- 514. 自由之路
- 546. 移除盒子
- 552. 学生出勤记录 II
- 600. 不含连续1的非负整数
- 629. K个逆序对数组
- 639. 解码方法 II
- 646. 最长数对链
- 664. 奇怪的打印机
- 689. 三个无重叠子数组的最大和
- 691. 贴纸拼词
题目讲解
53.
int maxSubArray(vector<int>& nums)
{
int dp = nums[0];//如果只有一个元素,则为它本身;
int result = dp;
for (int i = 1; i < nums.size(); ++i)
{
dp = max(nums[i], nums[i] + dp);//当前元素最大子序列和要么是它本身,要么是前面的最大和加它
result = max(result, dp);//最终的最大子序列和为:在当前元素最大子序列和前面所有元素最大子序列和取最大值
}
return result;
}
70.
int climbStairs(int n)
{
vector<int> v;
v.push_back(1);//1个台阶,有1种方法
v.push_back(2);//2个台阶,有2种方法
for (int i = 2; i < n; ++i)
{
v.push_back(v[i - 1] + v[i - 2]);//n个台阶的方法数=(n-1)个台阶方法数+(n-2)个台阶方法数
}
return v[n - 1];
}
//递归调用,会超时
if (n == 1)
return 1;
if (n == 2)
return 2;
return climbStairs(n - 1) + climbStairs(n - 2);
118.
vector<vector<int>> generate(int numRows)
{
vector<vector<int>> v;
v.resize(numRows);//vector采用[]赋值,需要提前指定vector大小
v[0].resize(1);
v[0][0] = 1;//第一行的元素为1
for (int i = 1; i < numRows; ++i)
{
v[i].resize(i + 1);
v[i][0] = 1;//第i行的第1个元素为1
int j = 1;
for (; j < i; ++j)
{
v[i][j] = v[i - 1][j - 1] + v[i - 1][j];//第i行的第j个元素=第i-1行的第j个元素+第i-1行的第j-1个元素
}
v[i][j] = 1;//第i行的最后一个元素为1
}
return v;
}
119.
vector<int> getRow(int rowIndex)
{
vector<vector<int>> v;
v.resize(rowIndex+1);//vector采用[]赋值,需要提前指定vector大小
v[0].resize(1);
v[0][0] = 1;//第一行的元素为1
for (int i = 1; i <= rowIndex; ++i)
{
v[i].resize(i + 1);
v[i][0] = 1;//第i行的第1个元素为1
int j = 1;
for (; j < i; ++j)
{
v[i][j] = v[i - 1][j - 1] + v[i - 1][j];//第i行的第j个元素=第i-1行的第j个元素+第i-1行的第j-1个元素
}
v[i][j] = 1;//第i行的最后一个元素为1
}
return v[rowIndex];//返回第rowIndex+1行的所有元素
}
121.
int maxProfit(vector<int>& prices) {
int min = prices[0];//记录前n-1天价格的最小值
int income = 0;//income=(买入-卖出)=-利润
for (int i = 1; i < prices.size(); ++i)
{
if (income > (min - prices[i]))//前n-1天最小的买入价格-第n天的价格=可以求出最大的利润
{
income = min - prices[i];
}
if (min > prices[i])
{
min = prices[i];//更新前n-1天最小的买入价格
}
}
return -income;
}
338.
vector<int> countBits(int n) {
vector<int> v;
v.resize(n + 1);
v[0] = 0;
for (int i = 1; i <= n; ++i)
{
if (i % 2 == 0)//n为偶数时,1比特个数=(n/2)的1比特个数
{
v[i] = v[i / 2];
}
else
{
v[i] = v[i - 1] + 1;//n为奇数时,1比特个数=(n-1)的1比特个数+1
}
}
return v;
}
392.
bool isSubsequence(string s, string t) {
int slength = s.length();
int tlength = t.length();
int j = 0;
for (int i = 0; i < slength; ++i)
{
for (; j < tlength; j++)
{
if (s[i] == t[j])//s[i]若在t中找到,则跳出循环
{
break;
}
}
if (j == tlength)//t序列指针到达末尾,说明未找到
{
return false;
}
j++;//找到了则t序列指针下移,为寻找s序列的下一个元素做准备
}
return true;
}
509.
int fib(int n) {
if (n == 0 || n == 1)
return n;
int mya = 0, myb = 1, myc;
for (int i = 2; i<= n; i++)//从n=2开始满足该规律
{
myc = mya + myb;
mya = myb;
myb = myc;
}
return myc;
}
230.
int kthSmallest(TreeNode* root, int k) {
stack<TreeNode*> s;
TreeNode* r = root;
while (r != NULL)
{
s.push(r);
r = r->left;
}
int count = 0;
while (!s.empty())
{
TreeNode* tmp = s.top();
s.pop();
count++;
if (count == k)
{
return tmp->val;
}
if (tmp->right != NULL)
{
tmp = tmp->right;
while (tmp != NULL)
{
s.push(tmp);
tmp = tmp->left;
}
}
}
return count;
}
22.
void findPare(int lPare, int rPare, int n, string s, vector<string>* v)
{
if (lPare + rPare == 2 * n)//左右括号都为n时,表示结束
{
v->push_back(s);
return;
}
if (rPare > lPare)//当右括号多余左括号时,违反规则
{
return;
}
if (rPare < lPare)//右括号少于左括号,便可以添加右括号
{
s = s + ')';
findPare(lPare, rPare + 1, n, s, v);
s.pop_back();//回溯时,需要将添加的右括号删除
}
if (lPare < n)//左括号只需要少于n,便可一直添加左括号
{
s= s + '(';
findPare(lPare + 1, rPare, n, s, v);
s.pop_back();
}
}
vector<string> generateParenthesis(int n) {
vector<string> v;
string s;
findPare(0,0,n,s,&v);
return v;
}
62.
写法一:
int uniquePaths(int m, int n) {
int arr[100][100];
for(int i=0;i<m;i++)//第一行只能横着走,只有一种方法到达
{
arr[i][0]=1;
}
for(int j=0;j<n;j++)//第一列只能竖着走,只有一种方法到达
{
arr[0][j]=1;
}
for(int i=1;i<m;i++)
{
for(int j=1;j<n;j++)
{
arr[i][j]=arr[i-1][j]+arr[i][j-1];//每个位置i,j的到达方法等于上面位置的到达方法+左边位置的到达方法
}
}
return arr[m-1][n-1];
}
写法二:
int uniquePaths(int m, int n) {
int arr[100][100]={0};//要初始化
arr[0][0]=1;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(i+1<m)
{
arr[i+1][j]+=arr[i][j];//遍历每一个i,j,计算右边的和下面的路径数
}
if(j+1<n)
{
arr[i][j+1]+=arr[i][j];
}
}
}
return arr[m-1][n-1];
}
63.
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int arr[100][100]={0};
if(obstacleGrid[0][0]==1)//如果该位置有障碍物,则该位置处的路径数为0
{
arr[0][0]=0;
}else
{
arr[0][0]=1;
}
for(int i=0;i<obstacleGrid.size();i++)
{
for(int j=0;j<obstacleGrid[0].size();j++)
{
if(i+1<obstacleGrid.size())
{
if(obstacleGrid[i+1][j]==1)
{
arr[i+1][j]=0;
}else
{
arr[i+1][j]+=arr[i][j];
}
}
if(j+1<obstacleGrid[0].size())
{
if(obstacleGrid[i][j+1]==1)
{
arr[i][j+1]=0;
}else
{
arr[i][j+1]+=arr[i][j];
}
}
}
}
return arr[obstacleGrid.size()-1][obstacleGrid[0].size()-1];
}
64.
int minPathSum(vector<vector<int>>& grid) {
int m=grid.size();
int n=grid[0].size();
int arr[200][200]={0};
arr[0][0]=grid[0][0];//(i,j)表示所有路径到达当前路径的最小值
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
int tmp1,tmp2;
if(i-1>=0)
{
tmp1=arr[i-1][j]+grid[i][j];
}
if(j-1>=0)
{
tmp2=arr[i][j-1]+grid[i][j];
}
if(i==0&&j!=0)
{
arr[i][j]=tmp2;
}
if(j==0&&i!=0)
{
arr[i][j]=tmp1;
}
if(i!=0&&j!=0)
{
arr[i][j]=min(tmp1,tmp2);//该最小值为min(上方位置的路径最小值,左边位置路径最小值)
}
}
}
return arr[m-1][n-1];
}
91.
int numDecodings(string s) {
int len=s.length();
vector<int> arr(len);
if(s[0]=='0') return 0;
arr[0]=1;
for(int i=1;i<len;i++)
{
if(s[i]!='0')
{
arr[i]+=arr[i-1];
}
if((s[i-1]=='1')||(s[i-1]=='2'&&s[i]<='6'))
{
if(i-2>=0)
arr[i]+=arr[i-2];
else
arr[i]++;
}
}
return arr[len-1];
}
122.
/*贪心:
如果你知道明天股价会跌,那你今天肯定不会入手;同理如果你知道明天会涨,那你今天肯定会买;*/
int maxProfit(vector<int>& prices) {
int tmp;
int sum=0;
for(int i=1;i<prices.size();i++)
{
tmp=prices[i]-prices[i-1];
if(tmp>0)
sum=sum+tmp;
}
return sum;
}
59.
int cuttingRope(int n) {
int* dp = new int[n+1];
dp[1]=1;
dp[2]=1;//如果绳子长2m,因为剪的段数至少为2个,所以只能时1*1
for(int i=3;i<=n;i++)//从绳子长为3开始计算,直到n
{
for(int j=2;j<i;j++)//每次剪得绳子长度至少为2,但又必须剪,所以小于i
{
dp[i]=max(dp[i],max(j*(i-j),j*dp[i-j]));
//内部max是比较剪了一段j长的绳子后,如果不减,和继续剪,哪个更大;
//外部max是比较这一次剪不同的j,哪次乘机最大;
}
}
return dp[n];
}