アルゴリズムによる質問を行いました。質問の難易度は、「シニア、ウェイ、スクール、ジェネラル」の4つのレベルに分かれています。このアルゴリズム問題のモジュールは、比較的長さが短いモジュールです。最初は問題の説明をすることです、そして次に私はこの問題をするために私の考えを使用します。今日はアルゴリズム問題の最初の問題です。最初に水を試してみましょう。
問題の説明(レベル:Wei)
有一个整型数组arr和一个大小为w的窗口从数组的最左边滑到最右边,窗口每次向右边滑一个位置。
例如,数组为[4,3,1,5,4,3,7,5],窗口大小为5时:
[4 3 1 5 4] 3 7 5 max = 5
4 [3 1 5 4 3] 7 5 max = 5
4 3 [1 5 4 3 7] 5 max = 7
4 3 1 [5 4 3 7 5] max = 7
即窗口最大值数组为 result = {5, 5,7,7}
回答:
問題については、私は通常、最初は暴力的な方法を使用してそれを行い、後でゆっくりと最適化することを考えます。
明らかに、この問題にブルートフォース法を使用するのは非常に簡単です。ウィンドウが1つ右に移動するたびに、ウィンドウ内のw要素を毎回トラバースし、この時点でウィンドウの最大値を見つけます。このメソッドの時間の複雑さはO(wn)です。コードは次のように表示されます。
//暴力法求解
public static int[] getMaxWindow(int[] arr, int w) {
if (w < 1 || arr == null || arr.length < w) {
return null;
}
int[] result = new int[arr.length - w + 1];
int index = 0;
//暴力求解直接从第 w-1个元素开始遍历
for (int i = w - 1; i < arr.length; i++) {
int max = arr[i];
//找出最大值
for (int k = i; k > i - w; k--) {
if (max < arr[k]) {
max = arr[k];
}
}
result[index++] = max;
}
return result;
}
注:左右に引くことができます
たとえば、今の例の配列について、誰もが質問について考えています。
最初のトラバーサルでは、最大= 5
2回目のトラバーサルでは、最大= 5
ブルートフォース方式を使用したときは、1回目でも2回目でも、ウィンドウ内のすべての要素を1回トラバースして最大値を見つけましたが、これは本当に必要ですか?
最初のトラバーサル中にmax = 5が見つかりました。したがって、2番目のトラバーサルでは、ウィンドウ範囲内で、max = 5の左側にある2つの数値1、3がまだ最大ですか?つまり、max = 5の左側にあるウィンドウ要素をトラバースする必要がありますか?
明らかに、max = 5の左側のウィンドウは、実際にはもうトラバースする必要はありません。つまり、ウィンドウの最大値にすることはできません。
そしてmax = 5、右側の4はウィンドウの最大値ですか?ウィンドウは引き続き右に移動するため、max = 5の右側のウィンドウ要素は、特定のウィンドウの最大値のままである可能性があります。
したがって、双方向キューを使用して、ウィンドウの最大値になる可能性のある添え字を記録できます。ここで可能であることに注意してください。
max = 5のように、その前の1,3をウィンドウの最大値にすることはできず、右側の4をウィンドウの最大値にすることもできます。そして、このキューは順序付けられ、キューの先頭は常にキュー内の最大値です。
この質問でデモンストレーションしましょう。result[]配列を使用して、ウィンドウの最大値を格納します。
1、結果[0] = 5
2、結果[1] = 5;
3、結果[2] = 7
7の前の5、4、および3をウィンドウの最大値にすることはできないため、他のすべてのキューをデキューする必要があります。
4、結果[3] = 7
トラバーサルが完了しました。このメソッドの時間の複雑さはO(n)です。
ここではアイデアと一般的な方法のみを提供しますが、特定のコードの実装で注意が必要な詳細はまだたくさんあります。実装コードを以下に示し、コードについて詳しく説明します。
//优化
public static int[] getMaxWindow2(int[] arr, int w) {
if (w < 1 || arr == null || arr.length < w) {
return null;
}
//用来保存成为最大窗口的元素
int[] result = new int[arr.length - w + 1];
int index = 0;
//用链表从当双向队列。
LinkedList<Integer> temp = new LinkedList<>();
//刚才演示的时候,我i直接从i = w-1那里开始演示了。
for (int i = 0; i < arr.length; i++) {
//如果队列不为空,并且存放在队尾的元素小于等于当前元素,那么
//队列的这个元素就可以弹出了,因为他不可能会是窗口最大值。
//【当前元素】指的是窗口向右移动的时候新加入的元素。
while (!temp.isEmpty() && arr[temp.peekLast()] <= arr[i]) {
temp.pollLast();//把队尾元素弹出
}
//把【当前元素】的下边加入到队尾
temp.addLast(i);
//如果队首的元素不在窗口范围内,则弹出
if (temp.peekFirst() == i - w) {
temp.pollFirst();//
}
if (i >= w - 1) {
//由于队首存放的是最大值,所以队首总是对应窗口的最大值元素
result[index++] = arr[temp.peekFirst()];
}
}
return result;
}
正直なところ、WeChatでコードを読むのは少し不快です。コンピューターで閲覧している方がいいです。スクリーンショットを使用するかどうかを検討していますが、スクリーンショットの場合、コードをコピーしたい場合はコードをコピーできない人もいます。コードをパッケージ化することを検討してください。そうすれば、バックグラウンドで取得するために返信します。
- 終了-
推奨読書:
時間の複雑さと空間の複雑さを学ぶために一歩一歩進んでください。
NATについて話す:何?グローバルIPとプライベートIPとは一体何ですか?