日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

每天一道LeetCode-----计算给定序列中所有长度为k的滑动窗的最大值集合

發布時間:2024/4/19 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 每天一道LeetCode-----计算给定序列中所有长度为k的滑动窗的最大值集合 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原題鏈接Sliding Window Maximum

給定一個數組,從左到右每k個位置算作一個滑動窗,每到達一個滑動窗,都需要找到這個滑動窗中最大的元素并記錄下來,最后返回所有最大元素組成的數組。要求時間復雜度在O(n)

首先,如果每到達一個滑動窗都計算一遍這個滑動窗覆蓋的區域的最大值,那么復雜度應該是….額…O(n*k - k*k)?k是滑動窗大小,n是數組元素個數


有沒有什么方法可以不用每次都計算一遍最大值呢

對于滑動窗而言,首先想到的就是向右移動的過程中是進行”左邊減,右邊加”的操作,即從滑動窗中刪除離開窗口的元素,并增加進去窗口的元素

利用這個特性,假設已經知道第一個滑動窗nums[0 : k - 1]中的最大值N,那么在向右移動時,刪掉nums[0],增加nums[k],然后將N和nums[k]的較大者作為第二個滑動窗的最大值

一切看起來非常完美,但是一旦N是nums[0],上面的想法突然變得,唔…,不太完善

不過現在任務倒是明朗不少,既然最大值N從窗口中離開,那么就需要知道在第一個滑動窗中第二大元素的值,拿它和nums[k]的較大者作為第二個滑動窗的最大值

問題是該怎么記,同時還要保證復雜度盡量低


假設…假設,有這么一個容器D,它記錄的是某些元素的下標,同時這個容器保證在這些下標上的元素的大小順序是遞減的,也就是nums[D[0]] > nums[D[1]] > nums[D[2]] ….

另外,對于這個容器,規定

  • 可以獲得第0的元素,即有成員函數D.front()
  • 可以獲得最后一個元素,即有成員函數D.back()
  • 可以彈出第一個元素,即有成員函數D.pop_front()
  • 可以彈出最后一個元素,即有成員函數D.pop_back()
  • 可以插入一個元素到末尾,即有成員函數D.push_back()

而對于這個容易的操作,規定

  • 插入的是元素下標而非元素的值
  • 插入元素下標到末尾時,從容器末尾開始依次彈出指向的元素值小于要插入下標代表的元素值的那些下標

最后一個規定多少有些繞口,用數字表示可能會清楚些,假設某一時刻容器D中的元素為

I1, I2, I3, I4, I5

注意根據上面的規定,可以知道nums[I1] > nums[I2] > nums[I3] > nums[I4] > nums[I5]

此時遍歷到一個元素M,它的下標為Im,那么如果要將Im插入D中時,需要從D的末尾開始,找到第一個下標Ii滿足nums[Ii] > M,至于Ii+1, …, I5就都pop掉,然后將Im追加到D的末尾。

假設I3是第一個滿足上面要求的值,那么插入Im后,D中元素為I1, I2, I3, Im。

I4和I5在向前找nums[Ii] > M的過程中已經被pop掉了


先提結論吧,根據上述規則,D中的下標分別是當前滑動窗第一大的元素,第二大的元素,…第n大的元素的下標。nums[D.front()]就是當前滑動窗覆蓋區域中的最大值

為什么是記錄下標而不是記錄元素的值,因為通過記錄下標,可以判斷上一個滑動窗的最大值是否已經被移出了

假設某一時刻的滑動窗位于nums[i-k+1, i-k+2, …, i-2, i-1, i],此時nums[i-k+1]是這個滑動窗的最大值,同時也知道D.front() == i-k+1。

那么當向右移動一步時,滑動窗變為nums[i-k+2, i-k+3, …, i-1, i, i+1]

要怎么才可以知道上一個滑動窗的最大值已經被移出了呢,通過(i+1) - D.front() == k啦:)

此時就可以把D.front()彈出,通過調用D.pop_front()。隨后把滑動窗新加進來的元素nums[i+1]追加到D中,追加之后,D.front()是當前滑動窗的最大值所在的位置

那么有沒有上面這樣的容器呢,沒有,唔…我是說C++標準庫里沒有,但是有個容器,它有所有容器D需要的成員函數,啊終于輪到deque出場了(一直以為它只是作為幕后工作者的身份用來實現stack, queue,沒想到還有這樣的用處)

deque,俗話叫雙端隊列,在隊列的兩端都可以進行插入刪除操作,并且復雜度是O(1)。不過對于上面的插入操作規則,需要手動去實現

實現完叫什么,單調隊列:)

代碼如下

class Solution { public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {vector<int> res;deque<int> dq;for(int i = 0; i < nums.size(); ++i){/* 上一個滑動窗的最大值被移出了,從容器中刪掉 */if(!dq.empty() && i - dq.front() == k)dq.pop_front();/* 將容器末尾的所有指向的元素值小于當前值的下標都刪掉 */while(!dq.empty() && nums[dq.back()] < nums[i])dq.pop_back();/* 將剛遍歷到的元素下標放進容器中 */dq.push_back(i);/* dq.front()是當前滑動窗第一大的元素的下標 *//* i >= k-1時才到達第一個滑動窗 */if(i >= k - 1)res.push_back(nums[dq.front()]);}return res;} };

本題主要利用deque實現單調隊列,不容易理解的地方在為什么可以在插入一個下標時可以將其他指向值小于當前值的下標都pop掉

考慮上面的D容器,容器內部情況為I1, I2, I3, I4, I5

這5個值表示當前滑動窗的第一大的元素是nums[I1],第二大的元素是nums[I2],…, 第五大的元素是nums[I5]

同時,當前的滑動窗為nums[I1, I2, I3, …, I5],假設右移一次后滑動窗為nums[I2, I3, …, I5, I],根據D可知右移前滑動窗的最大值nums[I1]已經被移走,第二大的元素是nums[I2],此時D的情況為

I2, I3, I4, I5

假設nums[I] > nums[I2],根據上述規則,更新后的D為

I

沒錯只有I一個,因為其他的下標代表的值都小于nums[I],都被pop掉了。

為什么可以只有I一個呢,因為I的位置是當前滑動窗最右邊的位置,只有右移k次后才會被移走,而在右移1, 2, …, k - 1時,都可以知道前k個滑動窗的最大值

總結

以上是生活随笔為你收集整理的每天一道LeetCode-----计算给定序列中所有长度为k的滑动窗的最大值集合的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。