0. トピック
循環配列 nums (nums[nums.length - 1] の次の要素は nums[0]) の場合、nums の各要素の次に大きい要素を返します。
数値 x の次に大きい要素は、配列の走査順序でこの数値の後の最初の大きい数値です。つまり、次に大きい数値を検索するにはループする必要があります。存在しない場合は、-1 を出力します。
例 1:
输入: nums = [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
例 2:
输入: nums = [1,2,3,4,3]
输出: [2,3,4,-1,4]
1. 私の解決策: 単調スタック
1.1 分析
この質問は、[LeetCode] 739、毎日の温度と同じです。難易度:中程度。さまざまな解決策があり、勉強する価値があります非常に似ているため、最初に 739 の質問を読むことをお勧めします
この問題が739問よりも難しい点は次の2点です。
- 配列は端から端までループする必要があるため、配列ループのトラバーサルを実装する方法を見つけてください。
- 特定の数値に対してそれより大きい数値がない場合は -1 が返されますが、実際には配列内の最大値の位置をすべて検索するためです (最大値は 1 より大きくなります)。
これらの問題には両方とも賢明な解決策があります。
- 配列を直接ループするのは非常に面倒です。別の考え方としては、配列をコピーして、元の配列の 2 倍の長さの新しい配列をアセンブルすることです。新しい配列を 1 回走査することは、元の配列を 2 回ループすることと同じではないでしょうか?
- 戻り値リストを -1 で初期化します。値の大きい位置は新しい値で上書きされ、最大値の位置は処理されず、-1 が保持されます。
このようにして両方の困難は解決され、次にどのような方法を使用するかを検討します。結論は次のとおりです。
「現在の値より大きい/小さい最も近い値を見つける」という問題については、単調スタックを使用して解決できます。
この種の問題に単調スタックが使用できるのはなぜでしょうか? 詳しい説明は「モノトニックスタックを使う理由」をご覧ください。「単調なスタック」を理解するための「単純な解決策」の観点から
1.2 単調スタックの考え方に従って直接書かれたプライマリコード
単調スタックの考え方に従って直接書かれたコードは次のとおりです。
class Solution:
def nextGreaterElements(self, nums: List[int]) -> List[int]:
# 实现循环的一种方法:将数组复制一遍;取前 nums 个结果即可
new_nums=nums*2
length=len(new_nums)
ans=[-1]*length # 为了避免单独判断最大的元素的位置,用-1初始化数组
stack=[]
for i in range(length):
if not stack:
stack.append([i,new_nums[i]])
else:
if new_nums[i]>stack[-1][1]:
while stack and new_nums[i]>stack[-1][1]:
ans[stack[-1][0]]=new_nums[i]
stack.pop()
stack.append([i,new_nums[i]])
else:
stack.append([i,new_nums[i]])
return ans[:length//2]
1.3 簡略化されたコード
明らかに、1.2 のコードは簡略化できます。簡略化されたコードは次のとおりです。
class Solution:
def nextGreaterElements(self, nums: List[int]) -> List[int]:
# 实现循环的一种方法:将数组复制一遍;取前 nums 个结果即可
new_nums=nums*2
length=len(new_nums)
ans=[-1]*length # 为了避免单独判断最大的元素的位置,用-1初始化数组
stack=[]
for i in range(length):
if not stack:
stack.append([i,new_nums[i]])
else:
while stack and new_nums[i]>stack[-1][1]:
ans[stack[-1][0]]=new_nums[i]
stack.pop()
stack.append([i,new_nums[i]])
return ans[:length//2]