LeetCodeDifficult-【面試題41. 數據流中的中位數】

如何得到一個數據流中的中位數?如果從數據流中讀出奇數個數值,那麼中位數就是所有數值排序之後位於中間的數值。如果從數據流中讀出偶數個數值,那麼中位數就是所有數值排序之後中間兩個數的平均值。
例如,
[2,3,4] 的中位數是 3
[2,3] 的中位數是 (2 + 3) / 2 = 2.5
設計一個支持以下兩種操作的數據結構:
void addNum(int num) - 從數據流中添加一個整數到數據結構中。
double findMedian() - 返回目前所有元素的中位數。

示例 1:
輸入:
[“MedianFinder”,“addNum”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[1],[2],[],[3],[]]
輸出:[null,null,null,1.50000,null,2.00000]

示例 2:
輸入:
[“MedianFinder”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[2],[],[3],[]]
輸出:[null,null,2.00000,null,2.50000]

限制:
最多會對 addNum、findMedia進行 50000 次調用。

注意:本題與主站 295 題相同:https://leetcode-cn.com/problems/find-median-from-data-stream/

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

思路1:二分查找

找中位數就是需要找排序好的list中的中位數,那麼就需要對list排序,在每次插入數據後,應該保證list有序,對於一個有序的list,我們插入一個數據的時間複雜度可以爲O(n)。包括:二分查找的合適的位置 O(logn),移動數據並插入O(n)。
找中位數就比較簡單,分一下奇偶數就行。
在這裏插入圖片描述

class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.nums = []

    def addNum(self, num: int) -> None:
        if self.nums == []:
            self.nums.append(num)
            return 
        def find(l, r, num):
            # 查找第一個不小於num的數的位置
            while l <= r:
                mid = (l + r) // 2 
                if self.nums[mid] < num:
                    l = mid + 1
                else:
                    r = mid - 1
            return l
        x = find(0, len(self.nums) - 1, num)
        # print(self.nums, num, x)
        self.nums = self.nums[0:x] + [num] + self.nums[x:]

    def findMedian(self) -> float:
        length = len(self.nums)
        if length & 1 == 0:
            return (self.nums[length // 2 - 1] + self.nums[length // 2]) / 2
        else:
            return self.nums[length // 2]


# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()

思路2:大根堆、小根堆

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
注意思考下,爲什麼每次打算加入一個堆的數據,必須先放入另一個堆,在彈出該堆的堆頂元素加入預加入的堆中。
在這裏插入圖片描述

class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.A = []  # 大根堆, 存儲較小部分,即排序好後的前部分(默認是小根堆,實現大根堆,只需要把數字全部取反就行了)
        self.B = []  # 小根堆,存儲較大部分,即排序好後的後部分

    def addNum(self, num: int) -> None:
        if len(self.A) == len(self.B):
            # heapq.heappush(self.B, num)  # 先放入B,得到B的最小值,再放入A中,保證A中的多一個數
            # heapq.heappush(self.A, -heapq.heappop(self.B))
            # 以上兩步可以合併爲一步:
            heapq.heappush(self.A, -heapq.heappushpop(self.B, num))
        else:
            # heapq.heappush(self.A, -num)  # 先放入A,得到A的最大值的相反數,再放入B中
            # heapq.heappush(self.B, -heapq.heappop(self.A))
            # 以上兩步可以合併爲一步:
            heapq.heappush(self.B, -heapq.heappushpop(self.A, -num))
        # print(self.A, self.B)
    def findMedian(self) -> float:
        if len(self.A) == len(self.B):
            return (-self.A[0] + self.B[0]) / 2
        else:
            return -self.A[0]

# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章