演算法-滑動視窗的中位元(堆)

來源:互聯網
上載者:User

標籤:偶數   compare   off   class   數字   隊列   bsp   返回   為什麼   

今天在網上刷了一道關於堆的題,感覺有所收穫。因為在這裡之前,之前從來沒有接觸過關於堆的題目。

  題意:

 給定一個包含 n 個整數的數組,和一個大小為 k 的滑動視窗,從左至右在數組中 滑動這個視窗,找到數組中每個視窗內的中位元。(如果數組個數是偶數,則在 該視窗排序數字後,返回第 N/2 個數字。)

範例:

 對於數組 [1,2,7,8,5], 滑動大小 k = 3 的視窗時,返回 [2,7,7]  最初,視窗的數組是這樣的:  [ | 1,2,7 | ,8,5] , 返回中位元 2;  接著,視窗繼續向前滑動一次。  [1, | 2,7,8 | ,5], 返回中位元 7;  接著,視窗繼續向前滑動一次。  [1,2, | 7,8,5 | ], 返回中位元 7;

  最初看到這個題,想到的方法是暴力,將給定的視窗裡面數字從先到大排序,再去中間那個數就行了。但是,到後面發現逾時了。於是乎,在網上搜尋了相關解法,網上大多數使用的是用優先隊列來操作。基本思路:用兩個優先隊列來模仿大頂堆和小頂堆(關於大頂堆和小頂堆的含義,這裡不再詳細的解釋),然後操作兩個隊列裡面的資料,選出中位元。

1.解題思想

  (1).優先隊列

   用一個優先隊列模仿大頂堆,用來裝已經在視窗裡面的數位小的那一半,另一個隊列則是小頂堆,裝另一半。於是乎,剩下的那一個(沒有進入任何一個隊列)就是中位元

  (2).最初的添加資料

  我們假設兩個隊列在初始化都是空的,同時最初的時候,視窗的裡面什麼資料都沒有,並且將當前的中位元設定為即將進入視窗的那個數。所以我們預設,向視窗加入資料是從第二個開始。

  當我們加入一個資料時,判斷它與當前的中位元(第一個資料作為中位元)的大小,如果它大於當前的中位元的話,則將它放入小頂堆;反之則放入大頂堆。放入過後(不論是大頂堆還是小頂堆),判斷大頂堆的size與小頂堆的size,如果大頂堆的size大於小頂堆的size,則將當前的中位元放入小頂堆,從大頂堆取出一個資料(poll)作為新的中位元;如果小頂堆的size - 1都大於大頂堆的size,那麼將當前的中位元放入大頂堆,從小頂堆中取出一個資料作為新的中位元。:

  

  問題: 為什麼這裡大頂堆的size大於小頂堆size就調整,而小頂堆的size - 1大於大頂堆的size才調整呢?

    我們先來看看題:如果數組個數是偶數,則在該視窗排序數字後,返回第 N/2 個數字。這裡說,如數字個數為偶數,則取 N/2。例如:視窗中有3個數字:1 2 3, 那中位元是 2,這個沒有爭議;但是如果視窗有4個數字:1 2 3 4 ,按照題意,選擇 N/2,則是2,也就是說當前大頂堆為:4,而小頂堆:1 2。所以在小頂堆的size - 1大於大頂堆的size才調整,因為如果不調整的話,當前的中位元與當前視窗的數位中位元不符合,因為視窗在這之前已經加入一個數字。

  而加入一個數字,中位元有兩種結果:當兩個頂堆的size相等或者小頂堆的size - 大頂堆的size = 1時,中位元不變,因為兩個頂堆的size平衡(我們可以這樣認為,當前視窗中數字小的那一半在視窗的左邊,大的那一半放在視窗的右邊,而中位元在兩個頂堆中間);當大頂堆的size大於小頂堆的size,表示實際的中位元偏向了大頂堆,所以將當前的中位元放入小頂堆去,從大頂堆中取出最大值作為當前的中位元(大頂堆中的所有數字小於當前的中位元,小頂堆的所有數字大於當前中位元,所以將當前的數字放入大頂堆)。總之一句話,就是加入一個數字後,必須保證當前的中位元就是實際的中位元。

  

  (3).後續的添加資料

    首先我們按照最初的添加資料步驟中得出的中位元,根據前面的規則來添加資料。

   當添加一個過後,我們必須在兩個頂堆中任意一個移除一個資料,這樣才能保證當前視窗顯示的數字。我們知道肯定移除當前視窗的第一個,怎麼移除呢?首先當前視窗的第一個的值,這個值與當前的中位元來比較,如果大於中位元,則這個值在小頂堆中,從小頂堆移除就是了;如果小於中位元,表示這個值在大頂堆中,從大頂堆中移除;當等於中位元時,則判斷當前小頂堆的size與大頂堆的size,如果大於大頂堆,則從小頂堆中取出一個資料作為新的中位元;反之,從大頂堆取出一個資料作為新的中位元。

  問題:為什麼當移除那個值與當前的中位元相等,要這樣操作?

  首先經過前一次添加資料,大頂堆的size與小頂堆的size要麼相等,要麼大頂堆的size - 小頂堆的.sze = 1。而這一次添加資料後,要麼加入大頂堆,要麼加入大頂堆,根據size判斷,當大頂堆的size >= 小頂堆的size,表示當前添加資料小於當前的中位元,則實際上的中位元偏向了大頂堆了,所以從大頂堆中取出一個資料作為新的中位元;反之也是這樣,從小頂堆中取出一個資料作為新的中位元也是這個道理。

  

 

(4).進一步的調整

  如果在(3)中,移除之前,大頂堆的size - 小頂堆的.sze = 1,而且移除的資料在大頂堆中,那麼實際的中位元就偏向了小頂堆,所以需要進一步調整;同時如果,本來大頂堆的size 等於 小頂堆的.sze ,而且移除的資料在小頂堆中,那麼實際的中位元就偏向大頂堆。

2.代碼

   說完瞭解釋,現在開始貼代碼

 1     public class minComparator implements Comparator<Integer> { 2         public int compare(Integer a, Integer b) { 3             if (a > b) 4                 return 1; 5             else if (a == b) 6                 return 0; 7             else 8                 return -1; 9         }10     }11 12     public class maxComparator implements Comparator<Integer> {13         public int compare(Integer a, Integer b) {14             if (a > b)15                 return -1;16             else if (a == b)17                 return 0;18             else19                 return 1;20         }21     }22 23     public List<Integer> medianSlidingWindow(int[] nums, int k) {24         List<Integer> res = new ArrayList<Integer>();25         if (k == 0 || nums.length < k) {26             return res;27         }28         PriorityQueue<Integer> maxQueue = new PriorityQueue<>();//大頂堆29         PriorityQueue<Integer> minQueue = new PriorityQueue<>();//小頂堆30         int media = nums[0];31         //最初的添加資料32         for (int i = 0; i < k; i++) {33             if (media < nums[i]) {34                 minQueue.offer(nums[i]);35             } else {36                 maxQueue.offer(nums[i]);37             }38             if (maxQueue.size() > minQueue.size()) {39                 minQueue.offer(media);40                 media = maxQueue.poll();41             } else if (maxQueue.size() < minQueue.size() - 1) {42                 maxQueue.offer(media);43                 media = maxQueue.poll();44             }45         }46         res.add(media);47         //後續的添加資料48         for (int i = 0; i < nums.length; i++) {49             if (media < nums[i]) {50                 minQueue.offer(nums[i]);51             } else {52                 maxQueue.offer(nums[i]);53             }54             //移除當前視窗第一個值55             int old = nums[i - k];56             if (old == media) {57                 if (minQueue.size() > maxQueue.size()) {58                     media = minQueue.poll();59                 } else {60                     media = maxQueue.poll();61                 }62             } else if (old < media) {63                 maxQueue.remove(old);64             } else {65                 minQueue.remove(old);66             }67             //進一步調整68             while (maxQueue.size() > minQueue.size()) {69                 minQueue.offer(media);70                 media = maxQueue.poll();71             }72             while (minQueue.size() < minQueue.size() - 1) {73                 maxQueue.offer(media);74                 media = minQueue.poll();75             }76             res.add(media);77         }78         return res;79     }

 

演算法-滑動視窗的中位元(堆)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.