Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
youngyangyang04 committed Nov 29, 2020
1 parent 77f45df commit 9f6e4e9
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 11 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
* [BAT级别技术面试流程和注意事项都在这里了](https://mp.weixin.qq.com/s/815qCyFGVIxwut9I_7PNFw)
* [深圳原来有这么多互联网公司,你都知道么?](https://mp.weixin.qq.com/s/Yzrkim-5bY0Df66Ao-hoqA)
* [北京有这些互联网公司,你都知道么?](https://mp.weixin.qq.com/s/FQTzoZtqXQ2rlS1UthGrag)
* [上海有这些互联网公司,你都知道么?](https://mp.weixin.qq.com/s/msqbX6eR2-JBQOYFfec4sg)

* 算法性能分析
* [究竟什么是时间复杂度,怎么求时间复杂度,看这一篇就够了](https://mp.weixin.qq.com/s/lYL9TSxLqCeFXIdjt4dcIw)
Expand Down Expand Up @@ -123,8 +124,7 @@
* [二叉树:一入递归深似海,从此offer是路人](https://mp.weixin.qq.com/s/PwVIfxDlT3kRgMASWAMGhA)
* [二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)
* [二叉树:前中后序迭代方式的写法就不能统一一下么?](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)
* [二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)
* [二叉树:你真的会翻转二叉树么?](https://mp.weixin.qq.com/s/6gY1MiXrnm-khAAJiIb5Bg)
* [二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog) * [二叉树:你真的会翻转二叉树么?](https://mp.weixin.qq.com/s/6gY1MiXrnm-khAAJiIb5Bg)
* [本周小结!(二叉树)](https://mp.weixin.qq.com/s/JWmTeC7aKbBfGx4TY6uwuQ)
* [二叉树:我对称么?](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)
* [二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)
Expand Down Expand Up @@ -175,6 +175,7 @@
* [本周小结!(回溯算法系列三)](https://mp.weixin.qq.com/s/tLkt9PSo42X60w8i94ViiA)
* [本周小结!(回溯算法系列三)续集](https://mp.weixin.qq.com/s/kSMGHc_YpsqL2j-jb_E_Ag)
* [视频来了!!带你学透回溯算法(理论篇)](https://mp.weixin.qq.com/s/wDd5azGIYWjbU0fdua_qBg)
* [视频来了!!回溯算法:组合问题](https://mp.weixin.qq.com/s/a_r5JR93K_rBKSFplPGNAA)
* [回溯算法:重新安排行程](https://mp.weixin.qq.com/s/3kmbS4qDsa6bkyxR92XCTA)
* [回溯算法:N皇后问题](https://mp.weixin.qq.com/s/lU_QwCMj6g60nh8m98GAWg)
* [回溯算法:解数独](https://mp.weixin.qq.com/s/eWE9TapVwm77yW9Q81xSZQ)
Expand Down Expand Up @@ -315,6 +316,7 @@
|[0454.四数相加II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0454.四数相加II.md) |哈希表 |中等| **哈希**|
|[0455.分发饼干](https://github.com/youngyangyang04/leetcode/blob/master/problems/0455.分发饼干.md) |贪心 |简单| **贪心**|
|[0459.重复的子字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0459.重复的子字符串.md) |字符创 |简单| **KMP**|
|[0474.一和零](https://github.com/youngyangyang04/leetcode/blob/master/problems/0474.一和零.md) |动态规划 |中等| **多重背包** 好题目|
|[0486.预测赢家](https://github.com/youngyangyang04/leetcode/blob/master/problems/0486.预测赢家.md) |动态规划 |中等| **递归** **记忆递归** **动态规划**|
|[0491.递增子序列](https://github.com/youngyangyang04/leetcode/blob/master/problems/0491.递增子序列.md) |深度优先搜索 |中等|**深度优先搜索/回溯算法** 这个去重有意思|
|[0496.下一个更大元素I](https://github.com/youngyangyang04/leetcode/blob/master/problems/0496.下一个更大元素I.md) ||中等|**单调栈** 入门题目,但是两个数组还是有点绕的|
Expand Down
115 changes: 106 additions & 9 deletions problems/0122.买卖股票的最佳时机II.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,79 @@
## 链接
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
> 贪心有时候比动态规划更巧妙,更好用!
## 思路
# 122.买卖股票的最佳时机II

首先要知道第0天买入,第3天卖出的利润:prices[3] - prices[0],相当于(prices[3] - prices[2]) + (prices[2] - prices[1]) + ()prices[1] - prices[0])
题目链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/

那么根据prices可以得到每天的利润序列:(prices[i] - prices[i - 1]).....(prices[1] - prices[0])。
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。


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

示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:
* 1 <= prices.length <= 3 * 10 ^ 4
* 0 <= prices[i] <= 10 ^ 4

# 思路

本题首先要清楚两点:

* 只有一只股票!
* 当前只有买股票或者买股票的操作

可以发现,我们需要收集每天的正利润就可以,收集正利润的区间,就是股票买卖的区间,而我们只需要关注最终利润就可以了,不需要记录区间
想获得利润至少要两天为一个交易单元

**这就是贪心所贪的地方,只收集正利润**
## 贪心算法

这道题目可能我们只会想,选一个低的买入,在选个高的卖,在选一个低的买入.....循环反复。

**如果想到其实最终利润是可以分解的,那么本题就很容易了!**

如果分解呢?

假如第0天买入,第3天卖出,那么利润为:prices[3] - prices[0]

相当于(prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])。

**此时就是把利润分解为每天为单位的维度,而不是从0天到第3天整体去考虑!**

那么根据prices可以得到每天的利润序列:(prices[i] - prices[i - 1]).....(prices[1] - prices[0])。

如图:

<img src='../pics/122.买卖股票的最佳时机II.png' width=600> </img></div>
![122.买卖股票的最佳时机II](https://img-blog.csdnimg.cn/2020112917480858.png)

一些同学陷入:第一天怎么就没有利润呢,第一天到底算不算的困惑中。

第一天当然没有利润,至少要第二天才会有利润,所以利润的序列比股票序列少一天!

从图中可以发现,其实我们需要收集每天的正利润就可以,**收集正利润的区间,就是股票买卖的区间,而我们只需要关注最终利润,不需要记录区间**

那么只收集正利润就是贪心所贪的地方!

**局部最优:收集每天的正利润,全局最优:求得最大利润**

局部最优可以推出全局最优,找不出反例,试一试贪心!

对应C++代码如下:

```
```C++
class Solution {
public:
int maxProfit(vector<int>& prices) {
Expand All @@ -29,6 +85,47 @@ public:
}
};
```
* 时间复杂度O(n)
* 空间复杂度O(1)
## 动态规划
动态规划将在下一个系列详细讲解,本题解先给出我的C++代码(带详细注释),感兴趣的同学可以自己先学习一下。
```C++
class Solution {
public:
int maxProfit(vector<int>& prices) {
// dp[i][1]第i天持有的最多现金
// dp[i][0]第i天持有股票后的最多现金
int n = prices.size();
vector<vector<int>> dp(n, vector<int>(2, 0));
dp[0][0] -= prices[0]; // 持股票
for (int i = 1; i < n; i++) {
// 第i天持股票所剩最多现金 = max(第i-1天持股票所剩现金, 第i-1天持现金-买第i天的股票)
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
// 第i天持有最多现金 = max(第i-1天持有的最多现金,第i-1天持有股票的最多现金+第i天卖出股票)
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
}
return max(dp[n - 1][0], dp[n - 1][1]);
}
};
```
* 时间复杂度O(n)
* 空间复杂度O(n)

# 总结

股票问题其实是一个系列的,属于动态规划的范畴,因为目前在讲解贪心系列,所以股票问题会在之后的动态规划系列中详细讲解。

**可以看出有时候,贪心往往比动态规划更巧妙,更好用,所以别小看了贪心算法**

**本题中理解利润拆分是关键点!** 不要整块的去看,而是把整体利润拆为每天的利润。

一旦想到这里了,很自然就会想到贪心了,即:只收集每天的正利润,最后稳稳的就是最大利润了。

就酱,「代码随想录」是技术公众号里的一抹清流,值得推荐给你的朋友同学们!


> 我是[程序员Carl](https://github.com/youngyangyang04),组队刷题可以找我,本文[leetcode刷题攻略](https://github.com/youngyangyang04/leetcode-master)已收录,更多[精彩算法文章](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzUxNjY5NTYxNA==&action=getalbum&album_id=1485825793120387074&scene=173#wechat_redirect)尽在:[代码随想录](https://img-blog.csdnimg.cn/20200815195519696.png),期待你的关注!

2 changes: 2 additions & 0 deletions problems/0300.最长上升子序列.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

## 思路
* dp[i]的定义

dp[i]表示i之前包括i的最长上升子序列。
Expand All @@ -11,6 +12,7 @@ dp[i]表示i之前包括i的最长上升子序列。

* 状态转移方程

位置i的最长升序子序列等于j从0到i-1各个位置的最长升序子序列 + 1 的最大值。

if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);

Expand Down
32 changes: 32 additions & 0 deletions problems/0474.一和零.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

搞不懂 leetcode后台是什么牛逼的编译器,初始化int dp[101][101] = {0}; 可以 ,int dp[101][101];就不行,有其他默认值,坑死。
代码我做了实验,后台会拿findMaxForm,运行两次,取第二次的结果,dp有上次记录的数值。

```
// 即使做了很多动态规划的题目,做这个依然懵逼
// 这道题目有点 程序员自己给自己出难进急转弯的意思
// 该子集中 最多 有 m 个 0 和 n 个 1 。 指的是整体子集
// 这是二维背包,多重背包
// dp[i][j] 有i个0,j个1最大有多少个子集,但是遍历的时候 顶部是哪里呢?
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
int dp[101][101] = {0}; // 默认初始化0
for (int i = 0; i < strs.size(); i++) {
int oneNum = 0, zeroNum = 0;
for (char c : strs[i]) {
if (c == '0') zeroNum++;
else oneNum++;
}
// 果然还是从后向前,模拟01背包
for (int j = m; j >= zeroNum; j--) {
for (int k = n; k >= oneNum; k--) {
dp[j][k] = max(dp[j][k], dp[j - zeroNum][k - oneNum] + 1);
}
}
}
return dp[m][n];
}
};
```

0 comments on commit 9f6e4e9

Please sign in to comment.