視窗和視窗內最大值的更新結構

2021-10-01 14:27:20 字數 3558 閱讀 7105

對於乙個陣列,我們有乙個l來表示視窗的最左邊,還有乙個r來表示最右邊。l和r只能向右移動,不能回退。l往右走是減數,r往右走就是加數,並且l也不能超過r。

如果你想要得到乙個視窗的最大值,那麼你當然可以通過遍歷的方法來實現,但這樣每次獲得視窗中最大值的代價就是遍歷的代價。如果想要o(1)的時間複雜度那麼可以繼續往下看。

視窗內最大值的更新結構使用的是雙向鍊錶(因為不需要遍歷),當我們需要加數的時候,如果先看鍊錶尾小於等於當前值的話就直接彈出鍊錶尾元素,直到當前鍊錶中末尾元素比當前值大或者當前鍊錶為空。如果鍊錶尾元素大於當前值的話那就直接加入到鍊錶尾。減數的更新邏輯如果移動了l,那麼就需要檢查頭元素的有效性,這也是我們為什麼要再鍊錶中同時儲存下標和對應的值的原因(其實只儲存下標是可以的,不管你用不用,陣列就在那兒)。視窗內儲存的所有值在視窗縮小的時候都有可能成為最大值的。而從尾部彈出的元素表示它沒有可能成為最大值了。

為什麼相等不重複壓入,而是彈出?

為了節省空間。

時間複雜度

使用這個結構的時間複雜度是o(n),但是我們找最大值並不是o(n)的,而是o(1)的。

面試題1

有乙個整型陣列arr和乙個大小為w的視窗從陣列的最左邊滑到最右邊,視窗每次向右滑乙個位置。請記錄下每個視窗的最大值到乙個陣列中。例如對於陣列[4,3,5,4,3,3,6,7]這樣的陣列,並指定視窗大小為3,那麼我們應該返回乙個內容為[5,5,5,4,6,7]這樣的陣列。要求時間複雜度o(n)

分析

我們搞乙個最大值的更新結構,每當我們遍歷到陣列中的某乙個位置的時候,只有當單向鍊錶的為空或者最後乙個值大於陣列當前元素的時候,那麼我們才可以直接加入到單向鍊錶中,否則就不斷的從單向鍊錶中彈出元素,直到滿足條件位置,每次迴圈都要判斷單向煉表頭是否過期了,如果過去了那麼我們就直接pop掉,最後我們要判斷是否形成寬為w的視窗了,如果形成了我們需要將單向鍊錶的頭元素加入進去。因為每個數都會進佇列一次和出佇列一次,所以複雜度o(n)。

示例**

#include

#include

#include

using

namespace std;

vector<

int>

getmaxwindow

(const vector<

int>

&arr,

int w);}

list<

int> qmax;

vector<

int> res;

for(

int i =

0; i < arr.

size()

; i++

) qmax.

push_back

(i);

// 如果過期,則減數

if(qmax.

front()

== i - w)

// 視窗形成則加入到res中

if(i >= w -1)

}return res;

}int

main

(int argc,

char

** ar**)

;int w =3;

vector<

int> res =

getmaxwindow

(arr, w)

;for

(auto e : res)

cout << endl;

system

("pause");

return exit_success;

}

面試題2

給定陣列arr和整數,共返回有多少個子陣列滿足如下條件:max(arr[i…j]) - min(arr[i…j]) <=num,max(arr[i…j]) 表示子陣列arr[i…j]中最大值,min(arr[i…j]) 表示子陣列arr[i…j]中最小值。要求陣列長度為n,請實現時間複雜度為o(n)的解法。

暴力解法

我們可以在雙重for迴圈中找到所有的子陣列,然後判斷該陣列是否滿足條件。

以上方法是o(n^3)的,兩個for,乙個遍歷。

示例**

bool

isvalid

(const vector<

int>

&arr,

int start,

int end,

int num)

return maxval - minval <= num;

}int

getnum1

(const vector<

int>

&arr,

int num)}}

return res;

}

視窗更新結構解法(時間複雜度o(n))

結論:如果乙個子陣列達標,那麼內部任何乙個子陣列都滿足條件。

如果乙個子陣列不達標,那麼包含該陣列的任何乙個子陣列都不滿足條件。

如果陣列長為n,那麼子陣列的個數為n+n-1+n-2+…1

思路:我們首先弄乙個最大值更新結構和乙個最小值更新結構,首先讓r移動到不能滿足條件之前的前乙個元素,所有的以l位置開始的所有子陣列都可以得到了,下一步我們將l向後移動,此時驗證下r是否可以向右移動,如果r可以向後移動了,那麼就一直移動到不滿足條件的節點的前乙個節點位置,如果不能r不能移動,那麼就繼續移動l。以此類推。時間複雜度o(n)。

示例**:

int

getnum2

(const vector<

int>

&arr,

int num)

list<

int> qmax;

list<

int> qmin;

int start =0;

int end =0;

int res =0;

while

(start < arr.

size()

) qmax.

push_back

(end)

;while

(!qmin.

empty()

&& arr[qmin.

back()

]>= arr[end]

) qmin.

push_back

(end);if

(arr[qmax.

front()

]- arr[qmin.

front()

]> num)

end++;}

if(qmin.

front()

== start)

if(qmax.

front()

== start)

res +

= end - start;

start++;}

return res;

}

滑動視窗內最大值

雙端佇列方法 佇列頭部一直都是當前窗內的最大元素下標 deque1為空或佇列尾元素大於等於當前元素,入佇列 佇列尾元素小於當前元素,彈出隊尾元素,確保佇列頭部是最大元素 若佇列尾索引減去佇列頭部索引大於k 1,彈出佇列頭部元素 暴力方法 vectorp 239 maxslidingwindow ve...

視窗以及視窗內最大值或最小值的更新結構

什麼是視窗?假設現在有一陣列及兩個指標l和r,兩指標只能在陣列上向右移動且不能回退,那麼 l r 這個區域即是乙個視窗。假設現在有一需求,即在視窗滑動的過程中我們需要求視窗內的最大值。這時我們需求借助最大值的更新結構,即雙端佇列。該更新結構需要保持其中的元素從大到小 從左到右 排序。加數過程 指標r...

滑動視窗最大值

題目描述 給定乙個陣列和滑動視窗的大小,找出所有滑動視窗裡數值的最大值。例如,如果輸入陣列及滑動視窗的大小3,那麼一共存在6個滑動視窗,他們的最大值分別為 針對陣列的滑動視窗有以下6個 幾個注意點 利用雙端佇列實現,如果後者比前者大,前者丟擲,後者進,如果比前者小,壓入佇列,判斷隊頭是否過期,這就需...