滑動窗口的中位數-大小堆解法

  1. 對於滑動窗口,需要求出每一次的中位數,最簡單暴力的方法就是對每一次的窗口內排序然後求中位數,此方法最簡單。
  2. 另外一種解法就是:將滑動窗口看成數據流(劍指offer上有這個題目),採用大小堆的方式來存在數據流,不過指定平衡策略要小心,各種情況都得考慮到。
    簡寫說法:左堆=大根堆 ; 右堆=小根堆

非平衡情況:

  1. len(左堆) = len(右堆)+2:將左堆的最大值移到右堆
  2. len(左堆) = len(右堆)-1:將右堆的最小值移到左堆
  3. len(左堆)<len(右堆)+1:不做調整
  4. len(左堆) or len(右堆)==0 :不做調整
  5. len(左堆) == len(右堆) or len(左堆) == len(右堆)+1:則需要判斷左右的最小值最大值是否合理,即左堆最大值是小於等於右堆最小值

主要弄清楚非平衡情況,然後設置平衡策略將左右堆進行不斷調整 來達到整個窗口的有序.

另外還需注意點:
因爲窗口時固定大小,所以每一次右移,需要刪除一個數,然後再添加一個數.

其他的就比較正常了,主要就是注意上述幾點.
代碼如下:

 class WindowsMidByHeap:
   def __init__(self, k: int = 0, ItemType=int):
       self.BigHeap = []
       self.SmallHeap = []
       self.ItemType = ItemType
       self.MAXSIZE = k

   def __len__(self):
       return len(self.BigHeap) + len(self.SmallHeap)

   def len(self):
       return self.__len__()

   def CheckType(self, n):
       # 檢查n的類型
       if isinstance(n, self.ItemType):
           return True
       else:
           return False

   def add(self, n, remove):
       # 向堆中添加一個元素,並移除一個元素
       if not self.CheckType(n):
           print("item's type must be {}".format(self.ItemType))
           return False
       if len(self.BigHeap) == 0 or self.len() < self.MAXSIZE:
           heapq.heappush(self.BigHeap, -1 * n)
           self.__blance()
           return True

       self.__remove(remove)
       heapq.heappush(self.BigHeap, -1 * n)
       self.__blance()

       return True

   def __blance(self):
       # 平衡左右堆,保持大小根堆(右堆)的大小 + 1 <= 大根堆(左堆)的而大小

       if len(self.BigHeap) == len(self.SmallHeap) - 1:
           temp = heapq.heappop(self.SmallHeap)
           heapq.heappush(self.BigHeap,-1 * temp)

       if len(self.BigHeap) == len(self.SmallHeap) + 2:
           temp = -1 * heapq.heappop(self.BigHeap)
           heapq.heappush(self.SmallHeap, temp)
           return

       # 平衡左右堆
       if len(self.BigHeap) < len(self.SmallHeap) + 1:
           return

       flag = len(self.BigHeap) == 0 or len(self.SmallHeap) == 0
       if flag:
           return

       len_flag = len(self.BigHeap) == len(self.SmallHeap) + 1 or len(self.BigHeap) == len(self.SmallHeap)

       if len_flag:

           Big_0 = -1 * self.BigHeap[0]
           Small_0 = self.SmallHeap[0]

           if Big_0 > Small_0:
               heapq.heappushpop(self.BigHeap, -1 * Small_0)
               heapq.heappushpop(self.SmallHeap, Big_0)
           return

   def __remove(self, n):
       # 從兩個堆中移除元素 n

       if n is None:
           return
       if -1 * n in self.BigHeap:
           self.BigHeap.remove(-1 * n)
           heapq.heapify(self.BigHeap)

       elif n in self.SmallHeap:
           self.SmallHeap.remove(n)
           heapq.heapify(self.SmallHeap)
       else:
           return False
       self.__blance()
       return True

   def MidNum(self):
       # 去兩個堆的中位數
       if self.len() < self.MAXSIZE:
           return None
       n1 = None
       n2 = None
       if len(self.BigHeap) == len(self.SmallHeap):
           n1 = self.SmallHeap[0] / 2.0
           n2 = -1 * self.BigHeap[0] / 2.0
       if len(self.BigHeap) == len(self.SmallHeap) + 1:
           n1 = -1 * self.BigHeap[0]
           n2 = 0

       return n1 + n2


class Solution():
    def medianSlidingWindow(self, nums: list, k: int) -> list:
       

        result = []
        wh = WindowsMidByHeap(k, int)
        for i, n in enumerate(nums):
            if len(wh) < k:
                wh.add(n, None)
            else:
                wh.add(n, nums[i - k])
            re = wh.MidNum()

            if re is not None:
                result.append(re)

        return result

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章