問題の説明
配列番号とスライディングウィンドウのサイズkが与えられた場合、すべてのスライディングウィンドウの中で最大値を見つけてください。
例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
問題解決のアイデア
この問題はブルートフォース法で解決できます。スライディングウィンドウがk個の数値の最大値に移動するたびに、O(k)時間かかるたびに、合計nステップ、そして
もちろんO(nk)は単調キューを使用して実行することもできます。単調キューは単調スタック問題に似ており、単調スタック問題はmin関数を含むスタック(単調スタック)を指します。
単調キューは単調スタック問題に似ています。キューに役に立たない要素があるかどうかを観察できます。使用された要素を削除すると、単調になるかどうかを確認します。
上の図は例であるため、スライディングウィンドウで毎回最小値を見つけたとします。-3が入ると、最初の3つは間違いなく役に立ちません。キューで最小値を見つけるたびに、-3は3,3。-3の左側にあるため、この3が最初に排出されます。つまり、-3が存在する限り、3が最小値として出力されることはなく、-3が長く存続している場合は出力されません。 、それはスライディングウィンドウから移動された後に3が移動されるので、前の3は回答として出力されず、削除できると結論付けることができます。同じことが-1にも当てはまります。
したがって、キュー内の前の番号が次の番号よりも大きい限り、次の番号は後でポップアウトされて小さくなるため、前の番号は間違いなく役に立ちません。したがって、このような逆のペアがある限り、大きなポイントを削除することができ、そのような番号をすべて削除すると、キュー全体が厳密に単調に増加するキューになります。
キューには最小値が必要であるため、厳密に単調に上昇するキューがhead要素であるため、最小値を見つけるたびに、head要素を直接見つけることができます。
要約:単調なスタックと単調なキューの問題については、最初にスタックとキューを使用して元の問題、つまり従来の考え方を激しくシミュレートすることを検討し、次にスタックとキューのどの要素が従来の考え方で役に立たないかを調べることができます、次にもう一度削除して、残りの要素が単調であるかどうかを確認します。残りの要素が単調である場合は、極値や二分法を使用するなど、いくつかの最適化を行うことができます。
この質問の場合:
要求する必要があるのはスライディングウィンドウの最大値であるため、現在のスライディングウィンドウに2つの添え字iとjがある場合、iはjの左側にあり(i <j)、に対応する要素はiはjに対応するものより大きくありません。の要素は(nums [i] <= nums [j])なので、どうなりますか?
スライディングウィンドウが右に移動するとき、iがまだウィンドウ内にある限り、jもウィンドウ内にある必要があります。これはjの左側にあるiによって保証されます。したがって、nums [j]が存在するため、nums [i]はスライディングウィンドウの最大値であってはなりません。nums[i]は完全に削除できます。
したがって、キューを使用して、削除されていないすべての添え字を格納できます。キューでは、これらの添え字は昇順で格納され、配列nums内の対応する値は厳密に単調に減少しています。キューに2つの隣接する添え字がある場合、それらの対応する値は等しいか増加しているため、前者をi、後者をjとします。これは、上記の状況に対応します。つまり、nums [i]は次のようになります。削除されました。これにより、矛盾が生じます。
スライディングウィンドウが右に移動したら、新しい要素をキューに入れる必要があります。キューの性質を維持するために、新しい要素をキューの最後にある要素と常に比較します。前者が後者以上の場合、キューの最後にある要素は次のようになります。完全に削除され、キューからポップされます。キューが空になるか、新しい要素がキューの最後の要素よりも小さくなるまで、この操作を続行する必要があります。
**キュー内の添え字に対応する要素は厳密に単調に減少しているため、この時点でキューの先頭にある添え字に対応する要素がスライディングウィンドウの最大値になります。**ただし、方法1と同様に、このときの最大値はスライディングウィンドウの左側の境界の左側にある可能性があり、ウィンドウが右に移動すると、スライディングウィンドウに表示されなくなります。したがって、チームのヘッド要素がウィンドウに表示されるまで、チームのヘッドから要素をポップし続ける必要もあります。
キューの最初と最後の要素を同時にポップするには、両端キューを使用する必要があります。この単調性を満たす両端キューは、一般に「単調キュー」と呼ばれます。
Javaでは、dequeとそのアプリケーションはJavadequeとそのアプリケーションを参照できます。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int len=nums.length;
if(len<1){
return nums;
}
//创建双端队列,使用LinkedList实现类
//注意,双端队列存储的是元素的下标
Deque<Integer> queue=new LinkedList<Integer>();
List<Integer> list=new ArrayList<Integer>(); //用于保存最终结果
int st=0,ed=k-1; //st,ed分别指向滑动窗口的左右边界
//我们先把初始情况下滑动窗口里面的元素都加入到单调队列中,这里隐含了k>=len的问题
for(int i=st;i<=ed;i++){
//当滑动窗口右移时,会不断判断当前元素是否大于或者等于队尾元素对应的值,如果满足条件的话,就移出队尾元素
//一直到队列为空或者当前元素小于队尾元素对应的值为止
//才把当前元素的下标加入到队尾,这样可以保持队列的单调递减的性质
while(!queue.isEmpty() && nums[i]>=nums[queue.peekLast()]){
int temp=queue.pollLast();
}
queue.offerLast(i);
}
//把队头元素加入到结果集中,队头元素对应的值即滑动窗口的最大值
list.add(nums[queue.peekFirst()]);
while(ed<len-1){
//滑动窗口后移一位
st++;
ed++;
//当队列不为空,并且队头元素(下标)处在滑动窗口左端点的左边时,就需要把它移出队列
if(!queue.isEmpty() && queue.peekFirst()<st){
int temp=queue.pollFirst();
}
//当滑动窗口右移时,会不断判断当前元素是否大于或者等于队尾元素对应的值,如果满足条件的话,就移出队尾元素
//一直到队列为空或者当前元素小于队尾元素对应的值为止
//才把当前元素的下标加入到队尾,这样可以保持队列的单调递减的性质
while(!queue.isEmpty() && nums[ed]>=nums[queue.peekLast()]){
int temp=queue.pollLast();
}
queue.offerLast(ed);
list.add(nums[queue.peekFirst()]);
}
//把结果存入到数组中
int size=list.size();
int res[]=new int[size];
for(int i=0;i<size;i++){
res[i]=list.get(i);
}
return res;
}
}