题目链接
前置知识
栈: 一种变形的数组结构,每次只能对栈顶进行操作
栈顶:数组的最后一个元素
栈底:数组的第一个元素
出栈即删除数组的最后一个元素
入栈则往数组尾部加入一个元素
单调栈: 基于栈结构,维护一个具有单调性的栈,要么是[1,2,3,4] 要么是[4,3,2,1]
如何维护单调性? 举个例子 我们要构造出一个单调递减栈
nums = [5,6,3,2,7] ,st代表栈
遍历nums, 因为栈中没有元素 我们先入栈5 st = [5]
nums[i] = 6 此时栈顶元素为 5 不大于6 所以不入栈
nums[i] = 3 此时栈顶元素为 5 大于 3, 入栈3,st = [5,3],此时栈顶为3
nums[i] = 2 此时栈顶元素为 3 大于 2, 入栈3,st = [5,3,2],此时栈顶为2
nums[i] = 7 此时栈顶元素为 2 不大于 7, 不入栈
循环结束,单调递减栈为 [5,3,2]
解题思路
-
分析题目,简化题意:我们需要找到一个最长的区间,满足左端点小于右端点即可
-
我们贪心来思考一下,怎么样会最大化这个区间? 必然是左端点尽量靠左 右端点尽量靠右
-
为什么不能双指针? 因为数组无序,不具备单调性
-
那我们想办法去构造出一个有序的集合,在通过类似双指针的思路就可以解决这个问题了!
-
我们维护一个值单调递减的栈(也可以数组模拟)st,保证其顺序为[4,3,2,1](存储的是下标即(0,1,2,3)), 然后反序遍历原数组,找到st中比他小的最后一个(不断出栈)
-
为什么逆序? 题目要求 i < j, 我们逆序一定满足这个条件 只需要判断 a[i] < a[j]即可
举个例子
A = [6,0,8,2,1,5]
构造st = [0,1] 对应的值为 [6,0]
反序遍历A数组,j作为下标
A[j] = 5时, A[st[n]] = 0 (n代表st最后一个下标) 此时满足题目要求 a[i] < a[j]
记录此时的长度 j - i并出栈,继续往前遍历
A[i] = 1 A[st[n]] = 6 不满足 继续遍历
A[i] = 8 A[st[n]] = 6 满足 但长度并没有A[j]= 5时长,所以答案不变,出栈
func solution(A []int) int {
//单调递减栈
st := []int{}
ans := 0
for i,v := range A {
//栈空 或 栈顶元素(前面的最小值)大于了当前元素,加入栈
if len(st) == 0 || A[st[len(st)-1]] >= v {
st = append(st, i)
}
}
for i := len(A) - 1; i >= 0; i-- {
j := i
//得到前面最远的最小值
for len(st) > 0 && A[st[len(st)-1]] <= A[i] {
j = st[len(st)-1]
st = st[:len(st)-1]
}
//记录最长区间
ans = max(ans, i - j)
}
return ans
}
def solution(A):
# 单调递减栈
st = []
ans = 0
for i, v in enumerate(A):
if not st or A[st[-1]] >= v:
st.append(i)
for i in range(len(A) - 1, -1, -1):
j = i
while st and A[st[-1]] <= A[i]:
j = st[-1]
st.pop()
ans = max(ans, i - j)
return ans
int solution(vector<int>A) {
// 单调递减栈
stack<int> st;
int ans = 0;
for (int i = 0; i < A.size(); i++) {
if (st.empty() || A[st.top()] >= A[i]) {
st.push(i);
}
}
for (int i = A.size() - 1; i >= 0; i--) {
int j = i;
while (!st.empty() && A[st.top()] <= A[i]) {
j = st.top();
st.pop();
}
ans = max(ans, i - j);
}
return ans;
}