-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Add monotonous stack #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
151 changes: 151 additions & 0 deletions
advanced_algorithm/monotonous_stack.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
# 单调栈 | ||
|
||
## 介绍 | ||
|
||
简单来讲单调栈就是里面所有值都是单调递增或者递减的栈,这里先拿递增来举例说明,下面是一个典型的单调递增栈的模板: | ||
|
||
``` | ||
for(int i = 0; i < A.size(); i++){ | ||
while(!in_stk.empty() && in_stk.top() > A[i]){ | ||
in_stk.pop(); | ||
} | ||
in_stk.push(A[i]); | ||
} | ||
``` | ||
|
||
这种数据结构能干什么呢?想象下如果有一个数组, `[3, 7, 8, 4]`,要在 O(N) 的时间内找到每个元素前面或者后面的最小值该怎么去做?这就是单调栈的作用 | ||
|
||
- 找到元素前面的最小值,`PLE`(Previous Less Element)。 | ||
|
||
以上面的数组为例: | ||
|
||
7 的 PLE 为 3, | ||
|
||
8 的 PLE 为 7, | ||
|
||
4 的 PLE 为 3, | ||
|
||
如果我们不直接把值塞进去,而是把数组下表扔进栈,那么我们就可以获取到一个新的 PLE 数组。如下: | ||
``` | ||
// previous_less[i] = j means A[j] is the previous less element of A[i]. | ||
// previous_less[i] = -1 means there is no previous less element of A[i]. | ||
vector<int> previous_less(A.size(), -1); | ||
for(int i = 0; i < A.size(); i++){ | ||
while(!in_stk.empty() && A[in_stk.top()] > A[i]){ | ||
in_stk.pop(); | ||
} | ||
previous_less[i] = in_stk.empty()? -1: in_stk.top(); | ||
in_stk.push(i); | ||
} | ||
``` | ||
|
||
- 找到元素后面的最小值,`NLE`(Next Less Element) | ||
|
||
同理可以获取到下面的模板 | ||
|
||
``` | ||
// next_less[i] = j means A[j] is the next less element of A[i]. | ||
// next_less[i] = -1 means there is no next less element of A[i]. | ||
vector<int> next_less(A.size(), -1); | ||
for(int i = 0; i < A.size(); i++){ | ||
while(!in_stk.empty() && A[in_stk.top()] > A[i]){ | ||
auto x = in_stk.top(); | ||
in_stk.pop(); | ||
next_less[x] = i; | ||
} | ||
in_stk.push(i); | ||
} | ||
``` | ||
|
||
## 应用 | ||
|
||
[sum-of-subarray-minimums](https://leetcode-cn.com/problems/sum-of-subarray-minimums/) | ||
|
||
> 子数组的最小值之和:给定一个整数数组 A,找到 min(B) 的总和,其中 B 的范围为 A 的每个(连续)子数组。 | ||
|
||
利用上述方法求出某个元素距离 `PLE` 和 `NLE` 之间的距离,然后对 `A[i]*left[i]*right[i]` 进行累加。 | ||
```go | ||
func sumSubarrayMins(A []int) int { | ||
mod := int(1e9+7) | ||
stack := make([]int, 0, len(A)) | ||
left, right := make([]int, len(A)), make([]int, len(A)) | ||
|
||
// 初始化 防止最小值没有处理的情况 | ||
for i := 0; i < len(A); i += 1 { | ||
left[i] = i + 1 | ||
right[i] = len(A)-i | ||
} | ||
|
||
for i := 0; i < len(A); i += 1 { | ||
for len(stack) != 0 && A[stack[len(stack)-1]] > A[i] { | ||
last := stack[len(stack)-1] | ||
right[last] = i - last | ||
stack = stack[:len(stack)-1] | ||
} | ||
if len(stack) != 0 { | ||
left[i] = i - stack[len(stack)-1] | ||
} | ||
stack = append(stack, i) | ||
} | ||
// 累加 | ||
var sum int | ||
for i := 0; i < len(A); i += 1 { | ||
sum = (sum + A[i]*left[i]*right[i]) % mod | ||
} | ||
return sum | ||
} | ||
``` | ||
|
||
[next-greater-element-ii](https://leetcode-cn.com/problems/next-greater-element-ii/) | ||
|
||
> 下一个更大元素 II:给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。 | ||
|
||
这是一个很基础的单调递减栈,主要的问题在于这个是一个循环数组,所以需要遍历两次,第二次遍历的时候不再入栈. | ||
```go | ||
func nextGreaterElements(nums []int) []int { | ||
if len(nums) == 0 { | ||
return nil | ||
} | ||
stack, ret := make([]int, 0, len(nums)), make([]int, len(nums)) | ||
// 第一遍遍历初始化 | ||
for i := range ret { | ||
ret[i] = -1 | ||
} | ||
// 这里会遍历两遍,只有第一次遍历的时候数据才会入栈 | ||
for i := 0; i < len(nums) * 2; i += 1 { | ||
|
||
i := i % len(nums) | ||
for len(stack) != 0 && nums[stack[len(stack)-1]] < nums[i] { | ||
ret[stack[len(stack)-1]] = nums[i] | ||
stack = stack[:len(stack)-1] | ||
} | ||
|
||
if i < len(nums) { | ||
stack = append(stack, i) | ||
} | ||
|
||
} | ||
return ret | ||
} | ||
``` | ||
|
||
[largest-rectangle-in-histogram](https://leetcode-cn.com/problems/largest-rectangle-in-histogram/) | ||
|
||
> 给定 _n_ 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 | ||
> 求在该柱状图中,能够勾勒出来的矩形的最大面积 | ||
|
||
在[栈的练习](../data_structure/stack_queue.md)中已经出现过了 | ||
|
||
## 练习 | ||
|
||
- [ ] [daily-temperatures](https://leetcode-cn.com/problems/daily-temperatures/) | ||
- [ ] [remove-k-digits](https://leetcode-cn.com/problems/remove-k-digits/) | ||
- [ ] [maximal-rectangle](https://leetcode-cn.com/problems/maximal-rectangle/) | ||
- [ ] [trapping-rain-water](https://leetcode-cn.com/problems/trapping-rain-water/)(challenge) | ||
- [ ] [remove-duplicate-letters](https://leetcode-cn.com/problems/remove-duplicate-letters/)(challenge) | ||
- [ ] [create-maximum-number](https://leetcode-cn.com/problems/create-maximum-number/) | ||
- [ ] [max-chunks-to-make-sorted-ii](https://leetcode-cn.com/problems/max-chunks-to-make-sorted-II/) | ||
- [ ] [sliding-window-maximum](https://leetcode-cn.com/problems/sliding-window-maximum/)(challenge) | ||
|
||
## Ref: | ||
- https://leetcode.com/problems/sum-of-subarray-minimums/discuss/178876/stack-solution-with-very-detailed-explanation-step-by-step |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.