通常是一维数组,要寻找任一个元素的 左边/右边 第一个 比自己大/小的元素的位置。此时我们就要想到可以用单调栈了。
寻找当前元素左侧第一个比它大/小的元素直接在栈里内侧寻找即可。
O ( n ) O(n) O(n)
单调栈的本质是空间换时间,用一个栈来记录遍历过的元素,但只需要遍历一边即可。
在遍历到当前元素的时候,需要把当前元素和栈口的元素进行比较,从而得到当前元素的某些性质。由于只能比较当前元素和栈口元素,所以对栈内的元素性质有要求:(从 stack top 到 stack bottom)单调增或者单调减。
array[i] > array[stack.top()]
array[i] = array[stack.top()]
array[i] < array[stack.top()]
题目链接 | 解题思路
本题要求当前元素和后面第一个比当前元素大的元素之间的距离。最直观的想法就是暴力两层 for loop,但时间复杂度 O ( n 2 ) O(n^2) O(n2) 太高了。
这里我们选择单调栈,本题是一道单调栈的基本应用。
由于要计算元素间的距离,我们在单调栈中选择储存下标。单调栈的意义是帮助我们存放已经遍历过的元素,从而能够比较当前元素和之前遍历过的元素(其实只能和栈口的元素比较,所以需要栈内保持递增或者递减的性质):<, >, ==
,得到当前元素是否满足某些要求的性质。
以本题的一个样例来演示:temperatures = [73,74,75,71,69,69,72,76,73]
(栈的开口朝右)
idx=0
| 当前栈为空 | stack = [0]
idx=1
| 当前栈为 stack=[0]
,t[1] > t[0]
| result[0] = 1 - 0, stack = [1]
(这样才能保持单调递增的栈)idx=2
| 当前栈为 stack=[1]
,t[2] > t[1]
| result[1] = 2 - 1, stack = [2]
idx=3
| 当前栈为 stack=[2]
,t[3] < t[2]
| stack = [2, 3]
idx=4
| 当前栈为 stack=[2, 3]
,t[4] < t[3]
| stack = [2, 3, 4]
idx=5
| 当前栈为 stack=[2, 3, 4]
,t[5] == t[4]
| stack = [2, 3, 4, 5]
idx=6
| 当前栈为 stack=[2, 3, 4, 5]
,t[6] > t[5], t[4], t[3], t[6] < t[2]
| result[5] = 6 - 5, result[4] = 6 - 4, result[3] = 6 - 3, stack = [2, 6]
idx=7
| 当前栈为 stack=[2, 6]
,t[7] > t[6], t[2]
| result[6] = 7 - 6, result[2] = 7 - 2, stack = [7]
idx=8
| 当前栈为 stack=[7]
,t[8] < t[7]
| stack = [7, 8]
result = [1, 1, 5, 3, 2, 1, 1, 0, 0]
(最后两个没有弹出栈,说明右边没有比他们更大的值了)所以本题对于单调栈的使用:
array[i] > array[stack.top()]
,则从栈中不断去除下标进行更新,直到当前值大于等于 stack top 或者栈为空,然后将当前元素压入栈中注意,答案中比较的是元素的值,但是栈中维护的只是下标,使用的时候不要搞混。
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
result = [0] * len(temperatures)
stack = [0]
for i in range(1, len(temperatures)):
if temperatures[i] <= temperatures[stack[-1]]:
stack.append(i)
else:
while (len(stack) > 0 and temperatures[i] > temperatures[stack[-1]]):
result[stack[-1]] = i - stack[-1]
stack.pop()
stack.append(i)
return result
直接两层遍历, O ( n 2 ) O(n^2) O(n2) 不出意外超时了。
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
result = []
for i in range(len(temperatures)):
curr_wait = 0
for j in range(i+1, len(temperatures)):
if temperatures[j] > temperatures[i]:
curr_wait = j - i
break
result.append(curr_wait)
return result
题目链接 | 解题思路
题目要求找到 nums1[i]
在 nums2
中下一个比当前元素大的元素,所以结果应该是一个长度和 nums1
相等的数组。
暴力解法是比较直观的,类似于双指针,时间复杂度为 O ( n 2 ) O(n^2) O(n2),可以看到这里面最大的时间复杂度来源于“在 nums2
中搜索每一个 nums1[i]
的位置”。乍一看这个搜索的时间是不可避免的成本,但在本题描述中,nums1
是 nums2
的一个子集,并且其中没有重复的元素。这样的条件可以通过合适的 hash mapping 来进行优化。具体如下:
val2idx = {}
for i in range(len(nums1)):
val2idx[nums1[i]] = i
这个 mapping 记录了 nums1
的 (nums1[i], i)
:对于在 nums1
内出现的 nums2[j]
,可以通过 val2idx[nums2[j]]
来得到在 nums1
的下标;否则,可以知道该 nums2[j]
没有在 nums1
内出现。
单调栈:本题的单调栈使用和上一题完全一样,依然需要找到当前元素右侧的第一个更大值。题目中虽然问的是“nums1[i]
在 nums2
中下一个比当前元素大的元素”,但可以通过拆分问题让其变得不这么复杂,只要得到 nums2
内部的右侧第一个更大值记录即可。
在得到了单调栈记录和**(值、下标)记录**之后,很容易就能得到最后结果。
nums2
中每个元素右侧第一个更大的元素下标。nums1
中的元素的 (nums1[i], i)
nums2
,如果当前值在 nums1
中出现过,就在 result
对应的位置记录 nums2_record[i]
class Solution:
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
# first find the index of the next greater number in nums2 for each element, -1 if no next greater
nums2_record = [-1] * len(nums2)
stack = [0]
for i in range(1, len(nums2)):
if nums2[i] <= nums2[stack[-1]]:
stack.append(i)
else:
while (len(stack) > 0 and nums2[i] > nums2[stack[-1]]):
nums2_record[stack[-1]] = i
stack.pop()
stack.append(i)
# find the mapping between index and value in nums1, since nums1 is a subset of nums2
val2idx = {}
for i in range(len(nums1)):
val2idx[nums1[i]] = i
result = [-1] * len(nums1)
for i in range(len(nums2)):
if nums2[i] in val2idx and nums2_record[i] != -1:
result[val2idx[nums2[i]]] = nums2[nums2_record[i]]
return result
虽然是一道简单题,但是想要高效完成就必须意识到hash + 单调栈的组合,还是很考验基本功的。
两层 for loop 循环, O ( n 2 ) O(n^2) O(n2) 的时间复杂度。
class Solution:
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
result = [-1] * len(nums1)
for i in range(len(nums1)):
find_duplicate = False
for j in range(len(nums2)):
if nums2[j] == nums1[i]:
find_duplicate = True
if nums2[j] > nums1[i] and find_duplicate:
result[i] = nums2[j]
break
return result