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]。 + +首先先将第一个遍历元素加入单调栈 +![739.每日温度1](https://img-blog.csdnimg.cn/20210219124434172.jpg) + +加入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]。 +![739.每日温度2](https://img-blog.csdnimg.cn/20210219124504299.jpg) + +加入T[2],同理,T[1]弹出 + +![739.每日温度3](https://img-blog.csdnimg.cn/20210219124527361.jpg) + +加入T[3],T[3] < T[2] (当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况),加T[3]加入单调栈。 + +![739.每日温度4](https://img-blog.csdnimg.cn/20210219124610761.jpg) + +加入T[4],T[4] == T[3] (当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况),此时依然要加入栈,不用计算距离,因为我们要求的是右面第一个大于本元素的位置,而不是大于等于! +![739.每日温度5](https://img-blog.csdnimg.cn/20210219124633444.jpg) + +加入T[5],T[5] > T[4] (当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况),将T[4]弹出,同时计算距离,更新result +![739.每日温度6](https://img-blog.csdnimg.cn/20210219124700567.jpg) + +T[4]弹出之后, T[5] > T[3] (当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况),将T[3]继续弹出,同时计算距离,更新result +![739.每日温度7](https://img-blog.csdnimg.cn/20210219124726613.jpg) + +直到发现T[5]小于T[st.top()],终止弹出,将T[5]加入单调栈 +![739.每日温度8](https://img-blog.csdnimg.cn/20210219124807715.jpg) + +加入T[6],同理,需要将栈里的T[5],T[2]弹出 +![739.每日温度9](https://img-blog.csdnimg.cn/2021021912483374.jpg) + +同理,继续弹出 +![739.每日温度10](https://img-blog.csdnimg.cn/2021021912490098.jpg) + +此时栈里只剩下了T[6] + +![739.每日温度11](https://img-blog.csdnimg.cn/20210219124930156.jpg) + +加入T[7], T[7] < T[6] 直接入栈,这就是最后的情况,result数组也更新完了。 +![739.每日温度12](https://img-blog.csdnimg.cn/20210219124957216.jpg) + + +此时有同学可能就疑惑了,那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 dailyTemperatures(vector& T) { + // 递减栈 + stack st; + vector result(T.size(), 0); + st.push(0); + for (int i = 1; i < T.size(); i++) { + if (T[i] < T[st.top()]) { // 情况一 + st.push(i); + } else if (T[i] == T[st.top()]) { // 情况二 + st.push(i); + } else { + while (!st.empty() && T[i] > T[st.top()]) { // 情况三 + result[st.top()] = i - st.top(); + st.pop(); + } + st.push(i); + } + } + return result; + } +}; +``` + +**建议一开始 都把每种情况分析好,不要上来看简短的代码,关键逻辑都被隐藏了**。 + +精简代码如下: + +```C++ +// 版本二 +class Solution { +public: + vector dailyTemperatures(vector& T) { + stack st; // 递减栈 + vector result(T.size(), 0); + st.push(0); + for (int i = 1; i < T.size(); i++) { + while (!st.empty() && T[i] > T[st.top()]) { // 注意栈不能为空 + result[st.top()] = i - st.top(); + st.pop(); + } + st.push(i); + + } + return result; + } +}; +``` +* 时间复杂度:O(n) +* 空间复杂度:O(n) + +精简的代码是直接把情况一二三都合并到了一起,其实这种代码精简是精简,但思路不是很清晰。 + +建议大家把情况一二三想清楚了,先写出版本一的代码,然后在其基础上在做精简! + + +## 其他语言版本 + + +Java: + +Python: + +Go: + + + + +----------------------- +* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) +* B站视频:[代码随想录](https://space.bilibili.com/525438321) +* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) +
+ diff --git a/problems/1035.不相交的线.md b/problems/1035.不相交的线.md index 039030d2..be159543 100644 --- a/problems/1035.不相交的线.md +++ b/problems/1035.不相交的线.md @@ -93,7 +93,18 @@ Java: ``` Python: - +```python +class Solution: + def maxUncrossedLines(self, A: List[int], B: List[int]) -> int: + dp = [[0] * (len(B)+1) for _ in range(len(A)+1)] + 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 + else: + dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + return dp[-1][-1] +``` Go: diff --git a/problems/哈希表总结.md b/problems/哈希表总结.md index a578a203..c3fbde2b 100644 --- a/problems/哈希表总结.md +++ b/problems/哈希表总结.md @@ -9,11 +9,10 @@ > 哈希表总结篇如约而至 -哈希表系列也是早期讲解的时候没有写总结篇,所以选个周末给补上,毕竟「代码随想录」的系列怎么能没有总结篇呢[机智]。 # 哈希表理论基础 -在[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA)中,我们介绍了哈希表的基础理论知识,不同于枯燥的讲解,这里介绍了都是对刷题有帮助的理论知识点。 +在[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)中,我们介绍了哈希表的基础理论知识,不同于枯燥的讲解,这里介绍了都是对刷题有帮助的理论知识点。 **一般来说哈希表都是用来快速判断一个元素是否出现集合里**。 @@ -29,7 +28,7 @@ * set(集合) * map(映射) -在C++语言中,set 和 map 都分别提供了三种数据结构,每种数据结构的底层实现和用途都有所不同,在[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA)中我给出了详细分析,这一知识点很重要! +在C++语言中,set 和 map 都分别提供了三种数据结构,每种数据结构的底层实现和用途都有所不同,在[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)中我给出了详细分析,这一知识点很重要! 例如什么时候用std::set,什么时候用std::multiset,什么时候用std::unordered_set,都是很有考究的。 @@ -41,13 +40,13 @@ 一些应用场景就是为数组量身定做的。 -在[哈希表:有效的字母异位词](https://mp.weixin.qq.com/s/vM6OszkM6L1Mx2Ralm9Dig)中,我们提到了数组就是简单的哈希表,但是数组的大小是受限的! +在[242.有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)中,我们提到了数组就是简单的哈希表,但是数组的大小是受限的! 这道题目包含小写字母,那么使用数组来做哈希最合适不过。 -在[哈希表:赎金信](https://mp.weixin.qq.com/s/sYZIR4dFBrw_lr3eJJnteQ)中同样要求只有小写字母,那么就给我们浓浓的暗示,用数组! +在[383.赎金信](https://mp.weixin.qq.com/s/qAXqv--UERmiJNNpuphOUQ)中同样要求只有小写字母,那么就给我们浓浓的暗示,用数组! -本题和[哈希表:有效的字母异位词](https://mp.weixin.qq.com/s/vM6OszkM6L1Mx2Ralm9Dig)很像,[哈希表:有效的字母异位词](https://mp.weixin.qq.com/s/vM6OszkM6L1Mx2Ralm9Dig)是求 字符串a 和 字符串b 是否可以相互组成,在[哈希表:赎金信](https://mp.weixin.qq.com/s/sYZIR4dFBrw_lr3eJJnteQ)中是求字符串a能否组成字符串b,而不用管字符串b 能不能组成字符串a。 +本题和[242.有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)很像,[242.有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)是求 字符串a 和 字符串b 是否可以相互组成,在[383.赎金信](https://mp.weixin.qq.com/s/qAXqv--UERmiJNNpuphOUQ)中是求字符串a能否组成字符串b,而不用管字符串b 能不能组成字符串a。 一些同学可能想,用数组干啥,都用map不就完事了。 @@ -56,7 +55,7 @@ ## set作为哈希表 -在[哈希表:两个数组的交集](https://mp.weixin.qq.com/s/N9iqAchXreSVW7zXUS4BVA)中我们给出了什么时候用数组就不行了,需要用set。 +在[349. 两个数组的交集](https://mp.weixin.qq.com/s/aMSA5zrp3jJcLjuSB0Es2Q)中我们给出了什么时候用数组就不行了,需要用set。 这道题目没有限制数值的大小,就无法使用数组来做哈希表了。 @@ -67,7 +66,7 @@ 所以此时一样的做映射的话,就可以使用set了。 -关于set,C++ 给提供了如下三种可用的数据结构:(详情请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA)) +关于set,C++ 给提供了如下三种可用的数据结构:(详情请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)) * std::set * std::multiset @@ -75,12 +74,12 @@ std::set和std::multiset底层实现都是红黑树,std::unordered_set的底层实现是哈希, 使用unordered_set 读写效率是最高的,本题并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set。 -在[哈希表:快乐数](https://mp.weixin.qq.com/s/G4Q2Zfpfe706gLK7HpZHpA)中,我们再次使用了unordered_set来判断一个数是否重复出现过。 +在[202.快乐数](https://mp.weixin.qq.com/s/n5q0ujxxrjQS3xuh3dgqBQ)中,我们再次使用了unordered_set来判断一个数是否重复出现过。 ## map作为哈希表 -在[哈希表:两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ)中map正式登场。 +在[1.两数之和](https://mp.weixin.qq.com/s/vaMsLnH-f7_9nEK4Cuu3KQ)中map正式登场。 来说一说:使用数组和set来做哈希法的局限。 @@ -89,7 +88,7 @@ std::set和std::multiset底层实现都是红黑树,std::unordered_set的底 map是一种``的结构,本题可以用key保存数值,用value在保存数值所在的下表。所以使用map最为合适。 -C++提供如下三种map::(详情请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA)) +C++提供如下三种map::(详情请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)) * std::map * std::multimap @@ -97,21 +96,21 @@ C++提供如下三种map::(详情请看[关于哈希表,你该了解这 std::unordered_map 底层实现为哈希,std::map 和std::multimap 的底层实现是红黑树。 -同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解),[哈希表:两数之和](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ)中并不需要key有序,选择std::unordered_map 效率更高! +同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解),[1.两数之和](https://mp.weixin.qq.com/s/vaMsLnH-f7_9nEK4Cuu3KQ)中并不需要key有序,选择std::unordered_map 效率更高! -在[哈希表:四数相加II](https://mp.weixin.qq.com/s/Ue8pKKU5hw_m-jPgwlHcbA)中我们提到了其实需要哈希的地方都能找到map的身影。 +在[454.四数相加](https://mp.weixin.qq.com/s/12g_w6RzHuEpFts1pT6BWw)中我们提到了其实需要哈希的地方都能找到map的身影。 -本题咋眼一看好像和[18. 四数之](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A),[15.三数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)差不多,其实差很多! +本题咋眼一看好像和[18. 四数之和](https://mp.weixin.qq.com/s/SBU3THi1Kv6Sar7htqCB2Q),[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)差不多,其实差很多! -**关键差别是本题为四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑重复问题,而[18. 四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g),[15.三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)是一个数组(集合)里找到和为0的组合,可就难很多了!** +**关键差别是本题为四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑重复问题,而[18. 四数之和](https://mp.weixin.qq.com/s/SBU3THi1Kv6Sar7htqCB2Q),[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)是一个数组(集合)里找到和为0的组合,可就难很多了!** 用哈希法解决了两数之和,很多同学会感觉用哈希法也可以解决三数之和,四数之和。 其实是可以解决,但是非常麻烦,需要去重导致代码效率很低。 -在[哈希表:解决了两数之和,那么能解决三数之和么?](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)中我给出了哈希法和双指针两个解法,大家就可以体会到,使用哈希法还是比较麻烦的。 +在[15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)中我给出了哈希法和双指针两个解法,大家就可以体会到,使用哈希法还是比较麻烦的。 -所以[18. 四数之](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A),[15.三数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)都推荐使用双指针法! +所以18. 四数之和,15.三数之和都推荐使用双指针法! # 总结 @@ -127,19 +126,6 @@ std::unordered_map 底层实现为哈希,std::map 和std::multimap 的底层 -## 其他语言版本 - - -Java: - - -Python: - - -Go: - - - ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) diff --git a/problems/背包理论基础01背包-1.md b/problems/背包理论基础01背包-1.md index de4508ad..852c489b 100644 --- a/problems/背包理论基础01背包-1.md +++ b/problems/背包理论基础01背包-1.md @@ -265,9 +265,46 @@ int main() { ## 其他语言版本 - Java: +```java + public static void main(String[] args) { + int[] weight = {1, 3, 4}; + int[] value = {15, 20, 30}; + int bagSize = 4; + testWeightBagProblem(weight, value, bagSize); + } + + public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){ + int wLen = weight.length, value0 = 0; + //定义dp数组:dp[i][j]表示背包容量为j时,前i个物品能获得的最大价值 + int[][] dp = new int[wLen + 1][bagSize + 1]; + //初始化:背包容量为0时,能获得的价值都为0 + for (int i = 0; i <= wLen; i++){ + dp[i][0] = value0; + } + //遍历顺序:先遍历物品,再遍历背包容量 + for (int i = 1; i <= wLen; i++){ + for (int j = 1; j <= bagSize; j++){ + if (j < weight[i - 1]){ + dp[i][j] = dp[i - 1][j]; + }else{ + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i - 1]] + value[i - 1]); + } + } + } + //打印dp数组 + for (int i = 0; i <= wLen; i++){ + for (int j = 0; j <= bagSize; j++){ + System.out.print(dp[i][j] + " "); + } + System.out.print("\n"); + } + } +``` + + + Python: diff --git a/problems/背包理论基础01背包-2.md b/problems/背包理论基础01背包-2.md index 1d3f8b03..e85d31b4 100644 --- a/problems/背包理论基础01背包-2.md +++ b/problems/背包理论基础01背包-2.md @@ -211,9 +211,35 @@ int main() { ## 其他语言版本 - Java: +```java + public static void main(String[] args) { + int[] weight = {1, 3, 4}; + int[] value = {15, 20, 30}; + int bagWight = 4; + testWeightBagProblem(weight, value, bagWight); + } + + public static void testWeightBagProblem(int[] weight, int[] value, int bagWeight){ + int wLen = weight.length; + //定义dp数组:dp[j]表示背包容量为j时,能获得的最大价值 + int[] dp = new int[bagWeight + 1]; + //遍历顺序:先遍历物品,再遍历背包容量 + for (int i = 0; i < wLen; i++){ + for (int j = bagWeight; j >= weight[i]; j--){ + dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]); + } + } + //打印dp数组 + for (int j = 0; j <= bagWeight; j++){ + System.out.print(dp[j] + " "); + } + } +``` + + + Python: