對於乙個陣列,我們有乙個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個 幾個注意點 利用雙端佇列實現,如果後者比前者大,前者丟擲,後者進,如果比前者小,壓入佇列,判斷隊頭是否過期,這就需...