Skip to content

Commit 353d43f

Browse files
committed
121
1 parent 315ff8d commit 353d43f

File tree

5 files changed

+172
-5
lines changed

5 files changed

+172
-5
lines changed

SUMMARY.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
* [99. Recover Binary Search Tree](leetcode-99-Recover-Binary-Search-Tree.md)
103103
* [100. Same Tree](leetcode-100-Same-Tree.md)
104104
* [leetcode 100 斩!回顾](leetcode100斩回顾.md)
105-
* [101 题到 120](leetcode-101-200.md)
105+
* [101 题到 121](leetcode-101-200.md)
106106
* [101. Symmetric Tree](leetcode-101-Symmetric-Tree.md)
107107
* [102. Binary Tree Level Order Traversal](leetcode-102-Binary-Tree-Level-Order-Traversal.md)
108108
* [103. Binary Tree Zigzag Level Order Traversal](leetcode-103-Binary-Tree-Zigzag-Level-Order-Traversal.md)
@@ -122,4 +122,5 @@
122122
* [117. Populating Next Right Pointers in Each Node II](leetcode-117-Populating-Next-Right-Pointers-in-Each-NodeII.md)
123123
* [118. Pascal's Triangle](leetcode-118-Pascal's-Triangle.md)
124124
* [119. Pascal's Triangle II](leetcode-119-Pascal's-TriangleII.md)
125-
* [120. Triangle](leetcode-120-Triangle.md)
125+
* [120. Triangle](leetcode-120-Triangle.md)
126+
* [121. Best Time to Buy and Sell Stock](leetcode-121-Best-Time-to-Buy-and-Sell-Stock.md)

leetCode-6-ZigZag-Conversion.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
## 解法一
1212

13-
按照写 Z 的过程,遍历每个字符,然后将字符存到对应的行中。用 goningDown 保存当前的遍历方向,如果遍历到两端,就改变方向。
13+
按照写 Z 的过程,遍历每个字符,然后将字符存到对应的行中。用 goingDown 保存当前的遍历方向,如果遍历到两端,就改变方向。
1414

1515
```java
1616
public String convert(String s, int numRows) {

leetcode-101-200.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,6 @@
3636

3737
<a href="leetcode-119-Pascal's-TriangleII.html">119. Pascal's Triangle II</a>
3838

39-
<a href="leetcode-120-Triangle.html">120. Triangle</a>
39+
<a href="leetcode-120-Triangle.html">120. Triangle</a>
40+
41+
<a href="leetcode-121-Best-Time-to-Buy-and-Sell-Stock.html">121. Best Time to Buy and Sell Stock</a>

leetcode-120-Triangle.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
[115 题](<https://leetcode.wang/leetcode-115-Distinct-Subsequences.html>) 已经进行了详细介绍,这里就粗略的记录了。
1414

15-
# 解法一 递归之分支
15+
# 解法一 递归之分治
1616

1717
求第 `0` 层到第 `n` 层的和最小,就是第`0`层的数字加上第`1`层到第`n`层的的最小和。
1818

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# 题目描述(简单难度)
2+
3+
![](https://windliang.oss-cn-beijing.aliyuncs.com/121.jpg)
4+
5+
给一个数组,看作每天股票的价格,然后某一天买入,某一天卖出,最大收益可以是多少。可以不操作,收入就是 `0`
6+
7+
# 解法一 暴力破解
8+
9+
先写个暴力的,看看对题目的理解对不对。用两个循环,外层循环表示买入时候的价格,内层循环表示卖出时候的价格,遍历所有的情况,期间更新最大的收益。
10+
11+
```java
12+
public int maxProfit(int[] prices) {
13+
int maxProfit = 0;
14+
for (int i = 0; i < prices.length; i++) {
15+
for (int j = i + 1; j < prices.length; j++) {
16+
maxProfit = Math.max(maxProfit, prices[j] - prices[i]);
17+
}
18+
}
19+
return maxProfit;
20+
}
21+
```
22+
23+
# 解法二 双指针
24+
25+
这种数组优化,经常就是考虑双指针的方法,从而使得两层循环变成一层。思考一下怎么定义指针的含义。
26+
27+
```java
28+
用两个指针, buy 表示第几天买入,sell 表示第几天卖出
29+
开始 buy,sell 都指向 0,表示不操作
30+
3 6 7 2 9
31+
^
32+
b
33+
^
34+
s
35+
36+
sell 后移表示这天卖出,计算收益是 6 - 3 = 3
37+
3 6 7 2 9
38+
^ ^
39+
b s
40+
41+
42+
sell 后移表示这天卖出,计算收益是 7 - 3 = 4
43+
3 6 7 2 9
44+
^ ^
45+
b s
46+
47+
sell 后移表示这天卖出,计算收益是 2 - 3 = -1
48+
3 6 7 2 9 12
49+
^ ^
50+
b s
51+
52+
此外,如上图,当前 sell 指向的价格小于了我们买入的价格,所以我们要把 buy 指向当前 sell 才会获得更大的收益
53+
原因很简单,收益的价格等于 prices[sell] - prices[buy],buy 指向 sell 会使得减数更小,
54+
所以肯定要选更小的 buy
55+
3 6 7 2 9 12
56+
^
57+
s
58+
^
59+
b
60+
61+
62+
sell 后移表示这天卖出,计算收益是 9 - 2 = 7
63+
这里也可以看出来减数从之前的 3 变成了 2,所以收益会更大
64+
3 6 7 2 9 12
65+
^ ^
66+
b s
67+
68+
sell 后移表示这天卖出,计算收益是 12 - 2 = 10
69+
3 6 7 2 9 12
70+
^ ^
71+
b s
72+
73+
然后在这些价格里选最大的就可以了。
74+
```
75+
76+
代码的话就很好写了。
77+
78+
```java
79+
public int maxProfit(int[] prices) {
80+
int maxProfit = 0;
81+
int buy = 0;
82+
int sell = 0;
83+
for (; sell < prices.length; sell++) {
84+
//当前价格更小了,更新 buy
85+
if (prices[sell] < prices[buy]) {
86+
buy = sell;
87+
} else {
88+
maxProfit = Math.max(maxProfit, prices[sell] - prices[buy]);
89+
90+
}
91+
}
92+
return maxProfit;
93+
}
94+
```
95+
96+
# 解法三
97+
98+
参考 <a href = "<https://leetcode.com/problems/best-time-to-buy-and-sell-stock/discuss/39038/Kadane's-Algorithm-Since-no-one-has-mentioned-about-this-so-far-%3A)-(In-case-if-interviewer-twists-the-input)>">这里</a> ,一个很新的角度。
99+
100+
先回忆一下 [53 题](<https://leetcode.wang/leetCode-53-Maximum-Subarray.html>),求子序列最大的和。
101+
102+
![img](https://windliang.oss-cn-beijing.aliyuncs.com/53.jpg)
103+
104+
当时的解法二,用动态规划,
105+
106+
用一个一维数组 `dp [ i ]` 表示以下标 `i` 结尾的子数组的元素的最大的和,也就是这个子数组最后一个元素是下边为 `i` 的元素,并且这个子数组是所有以 `i `结尾的子数组中,和最大的。
107+
108+
这样的话就有两种情况,
109+
110+
- 如果 `dp [ i - 1 ] < 0`,那么 `dp [ i ] = nums [ i ]`
111+
- 如果 `dp [ i - 1 ] >= 0`,那么 `dp [ i ] = dp [ i - 1 ] + nums [ i ]`
112+
113+
直接放一下最后经过优化后的代码,具体的可以过去 [看一下](<https://leetcode.wang/leetCode-53-Maximum-Subarray.html>)
114+
115+
```java
116+
public int maxSubArray(int[] nums) {
117+
int n = nums.length;
118+
int dp = nums[0];
119+
int max = nums[0];
120+
for (int i = 1; i < n; i++) {
121+
dp= Math.max(dp + nums[i],nums[i]);
122+
max = Math.max(max, dp);
123+
}
124+
return max;
125+
}
126+
```
127+
128+
而对于这道题我们可以转换成上边的问题。
129+
130+
对于数组 ` 1 6 2 8`,代表股票每天的价格。
131+
132+
定义一下转换规则,当前天的价格减去前一天的价格,第一天由于没有前一天,规定为 `0`,用来代表不操作。
133+
134+
数组就转换为 `0 6-1 2-6 8-2`,也就是 `0 5 -4 6`。现在的数组的含义就变成了股票相对于前一天的变化了。
135+
136+
现在我们只需要找出连续的和最大是多少就可以了,也就是变成了 `53` 题。
137+
138+
连续的和比如对应第 3 到 第 6 天加起来的和,那对应的买入卖出其实就是第 `2` 天买入,第 `6` 天卖出。
139+
140+
换句话讲,买入卖出和连续的和形成了互相映射,所以问题转换成功。
141+
142+
代码在上边的基础上改一下就可以了。
143+
144+
```java
145+
public int maxProfit(int[] prices) {
146+
int n = prices.length;
147+
int dp = 0;
148+
int max = 0;
149+
for (int i = 1; i < n; i++) {
150+
int num = prices[i] - prices[i - 1];
151+
dp = Math.max(dp + num, num);
152+
max = Math.max(max, dp);
153+
}
154+
return max;
155+
}
156+
```
157+
158+
而这个算法其实叫做 `Kadane` 算法,如果序列中含有负数,并且可以不选择任何一个数,那么最小的和也肯定是 `0`,也就是上边的情况,这也是把我们把第一天的浮动当作是 `0` 的原因。所以 `max `初始化成了 `0`
159+
160+
更多`Kadane` 算法的介绍可以参考 [维基百科](<https://zh.wikipedia.org/wiki/%E6%9C%80%E5%A4%A7%E5%AD%90%E6%95%B0%E5%88%97%E9%97%AE%E9%A2%98>)
161+
162+
#
163+
164+
这道题虽然是比较简单的,但是双指针的用法还是经常见的。另外解法三对问题的转换是真的佩服了。

0 commit comments

Comments
 (0)