【单调栈的定义】
满足单调性的栈结构
【图例】
【leetcode 1124 表现良好的最长时间段】
【题目】
给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数。
我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」。
所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。
请你返回「表现良好时间段」的最大长度。
示例 1:
输入:hours = [9,9,6,0,6,6,9]
输出:3
解释:最长的表现良好时间段是 [9,9,6]。
提示:
1 <= hours.length <= 10000
0 <= hours[i] <= 16
【分析】
第一步:根据时间是否大于8,将每一天的工作小时数量化成1与-1,这样有利于后续的计算。
题目中的[9,9,6,0,6,6,9]被量化为[1,1,-1,-1,-1,-1,1]。这样,理解题意,可以看出我们需要得到一个区间,在这个区间里面1和-1加起来要大于0。
第二步:怎样可以更方便的得到一个区间的和?
在这里,我们需要利用前缀和这个方法。利用前缀和方法,我们可以将[1,1,-1,-1,-1,-1,1]转化为前缀和prefixSrc = [0, 1, 2, 1, 0, -1, -2, -1],我们在前缀和前面加了一个0,是为了好操作。
如果要得到[1,3]区间的和,根据前缀和的定义,只需要使用prefixSrc[3]-prefixSrc[0]即可。
第三步:我们现在需要找到的是:区间和>0的最长区间。
那么可以这样思考,想要prefixSrc[j]大于prefixSrc[i],我们只需要找到比prefixSrc[j]更小的元素在哪里就可以了。这样就可以使用单调栈了。
我们先遍历一遍prefixSrc ,建议一个严格单调递减的单调栈,这样我们的栈顶元素就一定是prefixSrc 中的最小值。(其中,栈中存储的是元素的索引)
然后,我们从后向前遍历prefixSrc ,若遍历到的元素大于栈顶元素,则这个区间是符合规则的,需要记录下区间的大小,并且我们将栈顶元素弹出,然后继续比较当前遍历到的元素是不是大于栈顶元素。
图一
图二
图三
图四
图五
【python代码】
class Solution:
def longestWPI(self, hours: List[int]) -> int:
num = [ 0 for i in range(len(hours))]
for i in range(len(hours)):
if hours[i] > 8:
num[i] = 1
else:
num[i] = -1
j = 0
ans = []
ans.append(j)
for i in num:
j = j + i
ans.append(j)
stack = []
for i in range(len(num)):
if len(stack) == 0 or ans[stack[-1]] > ans[i]:
stack.append(i)
k = 0
n = len(hours)
while n > k:
while len(stack) != 0 and ans[stack[-1]] < ans[n]:
k = max(k,n-stack[-1])
stack.pop()
n -= 1
return k