diff --git a/README.md b/README.md index 4fa9100d..daef7c02 100644 --- a/README.md +++ b/README.md @@ -384,9 +384,12 @@ 54. [动态规划:最长回文子序列](./problems/0516.最长回文子序列.md) 55. [动态规划总结篇](./problems/动态规划总结篇.md) - (持续更新中....) +## 单调栈 + +1. [每日温度](./problems/0739.每日温度.md) + ## 图论 ## 十大排序 diff --git a/problems/0053.最大子序和(动态规划).md b/problems/0053.最大子序和(动态规划).md index e15f692e..dd0e513b 100644 --- a/problems/0053.最大子序和(动态规划).md +++ b/problems/0053.最大子序和(动态规划).md @@ -123,7 +123,19 @@ Java: ``` Python: - +```python +class Solution: + def maxSubArray(self, nums: List[int]) -> int: + if len(nums) == 0: + return 0 + dp = [0] * len(nums) + dp[0] = nums[0] + result = dp[0] + for i in range(1, len(nums)): + dp[i] = max(dp[i-1] + nums[i], nums[i]) #状态转移公式 + result = max(result, dp[i]) #result 保存dp[i]的最大值 + return result +``` Go: diff --git a/problems/0062.不同路径.md b/problems/0062.不同路径.md index 60b65818..47cb41af 100644 --- a/problems/0062.不同路径.md +++ b/problems/0062.不同路径.md @@ -308,6 +308,27 @@ func uniquePaths(m int, n int) int { } ``` +Javascript: +```Javascript +var uniquePaths = function(m, n) { + const dp = Array(m).fill().map(item => Array(n)) + + for (let i = 0; i < m; ++i) { + dp[i][0] = 1 + } + + for (let i = 0; i < n; ++i) { + dp[0][i] = 1 + } + + for (let i = 1; i < m; ++i) { + for (let j = 1; j < n; ++j) { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + } + } + return dp[m - 1][n - 1] +}; +``` ----------------------- diff --git a/problems/0063.不同路径II.md b/problems/0063.不同路径II.md index eeecad70..52f00322 100644 --- a/problems/0063.不同路径II.md +++ b/problems/0063.不同路径II.md @@ -279,6 +279,30 @@ func uniquePathsWithObstacles(obstacleGrid [][]int) int { ``` +Javascript +``` Javascript +var uniquePathsWithObstacles = function(obstacleGrid) { + const m = obstacleGrid.length + const n = obstacleGrid[0].length + const dp = Array(m).fill().map(item => Array(n).fill(0)) + + for (let i = 0; i < m && obstacleGrid[i][0] === 0; ++i) { + dp[i][0] = 1 + } + + for (let i = 0; i < n && obstacleGrid[0][i] === 0; ++i) { + dp[0][i] = 1 + } + + for (let i = 1; i < m; ++i) { + for (let j = 1; j < n; ++j) { + dp[i][j] = obstacleGrid[i][j] === 1 ? 0 : dp[i - 1][j] + dp[i][j - 1] + } + } + + return dp[m - 1][n - 1] +}; +``` ----------------------- diff --git a/problems/0115.不同的子序列.md b/problems/0115.不同的子序列.md index 1661acf8..e54389aa 100644 --- a/problems/0115.不同的子序列.md +++ b/problems/0115.不同的子序列.md @@ -148,7 +148,22 @@ Java: Python: - +```python +class Solution: + def numDistinct(self, s: str, t: str) -> int: + dp = [[0] * (len(t)+1) for _ in range(len(s)+1)] + for i in range(len(s)): + dp[i][0] = 1 + for j in range(1, len(t)): + dp[0][j] = 0 + for i in range(1, len(s)+1): + for j in range(1, len(t)+1): + 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[-1][-1] +``` Go: diff --git a/problems/0121.买卖股票的最佳时机.md b/problems/0121.买卖股票的最佳时机.md index d018efb7..259fff34 100644 --- a/problems/0121.买卖股票的最佳时机.md +++ b/problems/0121.买卖股票的最佳时机.md @@ -244,6 +244,47 @@ class Solution { // 动态规划解法 Python: +> 贪心法: +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + low = float("inf") + result = 0 + for i in range(len(prices)): + low = min(low, prices[i]) #取最左最小价格 + result = max(result, prices[i] - low) #直接取最大区间利润 + return result +``` + +> 动态规划:版本一 +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + length = len(prices) + if len == 0: + return 0 + dp = [[0] * 2 for _ in range(length)] + dp[0][0] = -prices[0] + dp[0][1] = 0 + for i in range(1, length): + dp[i][0] = max(dp[i-1][0], -prices[i]) + dp[i][1] = max(dp[i-1][1], prices[i] + dp[i-1][0]) + return dp[-1][1] +``` + +> 动态规划:版本二 +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + length = len(prices) + dp = [[0] * 2 for _ in range(2)] #注意这里只开辟了一个2 * 2大小的二维数组 + dp[0][0] = -prices[0] + dp[0][1] = 0 + for i in range(1, length): + dp[i % 2][0] = max(dp[(i-1) % 2][0], -prices[i]) + dp[i % 2][1] = max(dp[(i-1) % 2][1], prices[i] + dp[(i-1) % 2][0]) + return dp[(length-1) % 2][1] +``` Go: ```Go diff --git a/problems/0122.买卖股票的最佳时机II(动态规划).md b/problems/0122.买卖股票的最佳时机II(动态规划).md index ba277fdc..1215025e 100644 --- a/problems/0122.买卖股票的最佳时机II(动态规划).md +++ b/problems/0122.买卖股票的最佳时机II(动态规划).md @@ -171,6 +171,33 @@ class Solution Python: +> 版本一: +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + length = len(prices) + dp = [[0] * 2 for _ in range(length)] + dp[0][0] = -prices[0] + dp[0][1] = 0 + for i in range(1, length): + dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i]) #注意这里是和121. 买卖股票的最佳时机唯一不同的地方 + dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i]) + return dp[-1][1] +``` + +> 版本二: +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + length = len(prices) + dp = [[0] * 2 for _ in range(2)] #注意这里只开辟了一个2 * 2大小的二维数组 + dp[0][0] = -prices[0] + dp[0][1] = 0 + for i in range(1, length): + dp[i % 2][0] = max(dp[(i-1) % 2][0], dp[(i-1) % 2][1] - prices[i]) + dp[i % 2][1] = max(dp[(i-1) % 2][1], dp[(i-1) % 2][0] + prices[i]) + return dp[(length-1) % 2][1] +``` Go: diff --git a/problems/0123.买卖股票的最佳时机III.md b/problems/0123.买卖股票的最佳时机III.md index 24370d38..fccb187d 100644 --- a/problems/0123.买卖股票的最佳时机III.md +++ b/problems/0123.买卖股票的最佳时机III.md @@ -229,6 +229,40 @@ class Solution { // 动态规划 Python: +> 版本一: +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + if len(prices) == 0: + return 0 + dp = [[0] * 5 for _ in range(len(prices))] + dp[0][1] = -prices[0] + dp[0][3] = -prices[0] + for i in range(1, len(prices)): + dp[i][0] = dp[i-1][0] + dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]) + dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i]) + dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i]) + dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i]) + return dp[-1][4] +``` + +> 版本二: +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + if len(prices) == 0: + return 0 + dp = [0] * 5 + dp[1] = -prices[0] + dp[3] = -prices[0] + for i in range(1, len(prices)): + dp[1] = max(dp[1], dp[0] - prices[i]) + dp[2] = max(dp[2], dp[1] + prices[i]) + dp[3] = max(dp[3], dp[2] - prices[i]) + dp[4] = max(dp[4], dp[3] + prices[i]) + return dp[4] +``` Go: diff --git a/problems/0188.买卖股票的最佳时机IV.md b/problems/0188.买卖股票的最佳时机IV.md index f0adc237..431c292b 100644 --- a/problems/0188.买卖股票的最佳时机IV.md +++ b/problems/0188.买卖股票的最佳时机IV.md @@ -212,6 +212,20 @@ class Solution { //动态规划 Python: +```python +class Solution: + def maxProfit(self, k: int, prices: List[int]) -> int: + if len(prices) == 0: + return 0 + dp = [[0] * (2*k+1) for _ in range(len(prices))] + for j in range(1, 2*k, 2): + dp[0][j] = -prices[0] + for i in range(1, len(prices)): + for j in range(0, 2*k-1, 2): + dp[i][j+1] = max(dp[i-1][j+1], dp[i-1][j] - prices[i]) + dp[i][j+2] = max(dp[i-1][j+2], dp[i-1][j+1] + prices[i]) + return dp[-1][2*k] +``` Go: diff --git a/problems/0300.最长上升子序列.md b/problems/0300.最长上升子序列.md index 02105a7c..8b4ad5b1 100644 --- a/problems/0300.最长上升子序列.md +++ b/problems/0300.最长上升子序列.md @@ -130,7 +130,20 @@ class Solution { ``` Python: - +```python +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + if len(nums) <= 1: + return len(nums) + dp = [1] * len(nums) + result = 0 + for i in range(1, len(nums)): + for j in range(0, i): + if nums[i] > nums[j]: + dp[i] = max(dp[i], dp[j] + 1) + result = max(result, dp[i]) #取长的子序列 + return result +``` Go: ```go diff --git a/problems/0309.最佳买卖股票时机含冷冻期.md b/problems/0309.最佳买卖股票时机含冷冻期.md index 44ca2b26..4667c122 100644 --- a/problems/0309.最佳买卖股票时机含冷冻期.md +++ b/problems/0309.最佳买卖股票时机含冷冻期.md @@ -189,6 +189,21 @@ class Solution { Python: +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + n = len(prices) + if n == 0: + return 0 + dp = [[0] * 4 for _ in range(n)] + dp[0][0] = -prices[0] #持股票 + for i in range(1, n): + dp[i][0] = max(dp[i-1][0], max(dp[i-1][3], dp[i-1][1]) - prices[i]) + dp[i][1] = max(dp[i-1][1], dp[i-1][3]) + dp[i][2] = dp[i-1][0] + prices[i] + dp[i][3] = dp[i-1][2] + return max(dp[n-1][3], dp[n-1][1], dp[n-1][2]) +``` Go: diff --git a/problems/0406.根据身高重建队列.md b/problems/0406.根据身高重建队列.md index eec9680b..9aca5b94 100644 --- a/problems/0406.根据身高重建队列.md +++ b/problems/0406.根据身高重建队列.md @@ -226,7 +226,25 @@ class Solution: Go: +Javascript: +```Javascript +var reconstructQueue = function(people) { + let queue = [] + people.sort((a, b ) => { + if(b[0] !== a[0]) { + return b[0] - a[0] + } else { + return a[1] - b[1] + } + + }) + for(let i = 0; i < people.length; i++) { + queue.splice(people[i][1], 0, people[i]) + } + return queue +}; +``` ----------------------- diff --git a/problems/0452.用最少数量的箭引爆气球.md b/problems/0452.用最少数量的箭引爆气球.md index 9b0cc925..bb3ebbdc 100644 --- a/problems/0452.用最少数量的箭引爆气球.md +++ b/problems/0452.用最少数量的箭引爆气球.md @@ -175,7 +175,24 @@ class Solution: Go: +Javascript: +```Javascript +var findMinArrowShots = function(points) { + points.sort((a, b) => { + return a[0] - b[0] + }) + let result = 1 + for(let i = 1; i < points.length; i++) { + if(points[i][0] > points[i - 1][1]) { + result++ + } else { + points[i][1] = Math.min(points[i - 1][1], points[i][1]) + } + } + return result +}; +``` ----------------------- diff --git a/problems/0674.最长连续递增序列.md b/problems/0674.最长连续递增序列.md index 636ee0cc..31ab6b0e 100644 --- a/problems/0674.最长连续递增序列.md +++ b/problems/0674.最长连续递增序列.md @@ -184,6 +184,37 @@ Java: Python: +> 动态规划: +```python +class Solution: + def findLengthOfLCIS(self, nums: List[int]) -> int: + if len(nums) == 0: + return 0 + result = 1 + dp = [1] * len(nums) + for i in range(len(nums)-1): + if nums[i+1] > nums[i]: #连续记录 + dp[i+1] = dp[i] + 1 + result = max(result, dp[i+1]) + return result +``` + +> 贪心法: +```python +class Solution: + def findLengthOfLCIS(self, nums: List[int]) -> int: + if len(nums) == 0: + return 0 + result = 1 #连续子序列最少也是1 + count = 1 + for i in range(len(nums)-1): + if nums[i+1] > nums[i]: #连续记录 + count += 1 + else: #不连续,count从头开始 + count = 1 + result = max(result, count) + return result +``` Go: diff --git a/problems/0714.买卖股票的最佳时机含手续费(动态规划).md b/problems/0714.买卖股票的最佳时机含手续费(动态规划).md index 14b5859b..5eb3453b 100644 --- a/problems/0714.买卖股票的最佳时机含手续费(动态规划).md +++ b/problems/0714.买卖股票的最佳时机含手续费(动态规划).md @@ -139,7 +139,17 @@ public int maxProfit(int[] prices, int fee) { ``` Python: - +```python +class Solution: + def maxProfit(self, prices: List[int], fee: int) -> int: + n = len(prices) + dp = [[0] * 2 for _ in range(n)] + dp[0][0] = -prices[0] #持股票 + for i in range(1, n): + dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i]) + dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i] - fee) + return max(dp[-1][0], dp[-1][1]) +``` Go: diff --git a/problems/0718.最长重复子数组.md b/problems/0718.最长重复子数组.md index be9109b2..204a513b 100644 --- a/problems/0718.最长重复子数组.md +++ b/problems/0718.最长重复子数组.md @@ -158,6 +158,36 @@ Java: Python: +> 动态规划: +```python +class Solution: + def findLength(self, A: List[int], B: List[int]) -> int: + dp = [[0] * (len(B)+1) for _ in range(len(A)+1)] + result = 0 + for i in range(1, len(A)+1): + for j in range(1, len(B)+1): + if A[i-1] == B[j-1]: + dp[i][j] = dp[i-1][j-1] + 1 + result = max(result, dp[i][j]) + return result +``` + +> 动态规划:滚动数组 +```python +class Solution: + def findLength(self, A: List[int], B: List[int]) -> int: + dp = [0] * (len(B) + 1) + result = 0 + for i in range(1, len(A)+1): + for j in range(len(B), 0, -1): + if A[i-1] == B[j-1]: + dp[j] = dp[j-1] + 1 + else: + dp[j] = 0 #注意这里不相等的时候要有赋0的操作 + result = max(result, dp[j]) + return result +``` + Go: ```Go diff --git a/problems/0739.每日温度.md b/problems/0739.每日温度.md new file mode 100644 index 00000000..516d068d --- /dev/null +++ b/problems/0739.每日温度.md @@ -0,0 +1,196 @@ + +
+欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+ +# 739. 每日温度 + + +https://leetcode-cn.com/problems/daily-temperatures/ + +请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。 + +例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。 + +提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。 + + +## 思路 + +首先想到的当然是暴力解法,两层for循环,把至少需要等待的天数就搜出来了。时间复杂度是O(n^2) + +那么接下来在来看看使用单调栈的解法。 + +那有同学就问了,我怎么能想到用单调栈呢? 什么时候用单调栈呢? + +**通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了**。 + +时间复杂度为O(n)。 + +例如本题其实就是找找到一个元素右边第一个比自己大的元素。 + +此时就应该想到用单调栈了。 + +那么单调栈的原理是什么呢?为什么时间复杂度是O(n)就可以找到每一个元素的右边第一个比它大的元素位置呢? + +单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素的元素,优点是只需要遍历一次。 + + +在使用单调栈的时候首先要明确如下几点: + +1. 单调栈里存放的元素是什么? + +单调栈里只需要存放元素的下标i就可以了,如果需要使用对应的元素,直接T[i]就可以获取。 + +2. 单调栈里元素是递增呢? 还是递减呢? + +**注意一下顺序为 从栈头到栈底的顺序**,因为单纯的说从左到右或者从前到后,不说栈头朝哪个方向的话,大家一定会越看越懵。 + + +这里我们要使用递增循序(再强调一下是指从栈头到栈底的顺序),因为只有递增的时候,加入一个元素i,才知道栈顶元素在数组中右面第一个比栈顶元素大的元素是i。 + +文字描述理解起来有点费劲,接下来我画了一系列的图,来讲解单调栈的工作过程。 + +使用单调栈主要有三个判断条件。 + +* 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况 +* 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况 +* 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况 + +**把这三种情况分析清楚了,也就理解透彻了**。 + +接下来我们用temperatures = [73, 74, 75, 71, 71, 72, 76, 73]为例来逐步分析,输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。 + +首先先将第一个遍历元素加入单调栈 + + +加入T[1] = 74,因为T[1] > T[0](当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况),而我们要保持一个递增单调栈(从栈头到栈底),所以将T[0]弹出,T[1]加入,此时result数组可以记录了,result[0] = 1,即T[0]右面第一个比T[0]大的元素是T[1]。 + + +加入T[2],同理,T[1]弹出 + + + +加入T[3],T[3] < T[2] (当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况),加T[3]加入单调栈。 + + + +加入T[4],T[4] == T[3] (当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况),此时依然要加入栈,不用计算距离,因为我们要求的是右面第一个大于本元素的位置,而不是大于等于! + + +加入T[5],T[5] > T[4] (当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况),将T[4]弹出,同时计算距离,更新result + + +T[4]弹出之后, T[5] > T[3] (当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况),将T[3]继续弹出,同时计算距离,更新result + + +直到发现T[5]小于T[st.top()],终止弹出,将T[5]加入单调栈 + + +加入T[6],同理,需要将栈里的T[5],T[2]弹出 + + +同理,继续弹出 + + +此时栈里只剩下了T[6] + + + +加入T[7], T[7] < T[6] 直接入栈,这就是最后的情况,result数组也更新完了。 + + + +此时有同学可能就疑惑了,那result[6] , result[7]怎么没更新啊,元素也一直在栈里。 + +其实定义result数组的时候,就应该直接初始化为0,如果result没有更新,说明这个元素右面没有更大的了,也就是为0。 + +以上在图解的时候,已经把,这三种情况都做了详细的分析。 + +* 情况一:当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况 +* 情况二:当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况 +* 情况三:当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况 + +C++代码如下: + +```C++ +// 版本一 +class Solution { +public: + vector