如何使用 Python 堆实现动态中位数问题?

2023-04-11 00:00:00 动态 如何使用 中位数

动态中位数问题即为在一个数列中,动态地插入和删除数字,求得数列的中位数。其中,中位数指的是有序数列中,中间位置的数字。如果数列长度为偶数,则中位数为中间两个数的平均值。

使用堆来实现动态中位数问题,需要维护两个堆:一个最大堆和一个最小堆。最大堆中存储的是数列中较小的一半数字,最小堆中存储的是数列中较大的一半数字。此时,如果两个堆的大小不同,则中位数为堆顶元素较多的那个堆的堆顶。如果两个堆的大小相同,则中位数为两个堆顶元素的平均值。

具体实现如下:

import heapq

class DynamicMedian:
    def __init__(self):
        self.max_heap = []  # 最大堆,存储数列中较小的一半数字
        self.min_heap = []  # 最小堆,存储数列中较大的一半数字

    def insert(self, num):
        if not self.max_heap or num <= -self.max_heap[0]:
            heapq.heappush(self.max_heap, -num)
        else:
            heapq.heappush(self.min_heap, num)

        # 平衡两个堆的大小,使得差值不大于 1
        if len(self.max_heap) - len(self.min_heap) > 1:
            temp = -heapq.heappop(self.max_heap)
            heapq.heappush(self.min_heap, temp)
        elif len(self.min_heap) - len(self.max_heap) > 1:
            temp = heapq.heappop(self.min_heap)
            heapq.heappush(self.max_heap, -temp)

    def find_median(self):
        # 两个堆中的元素个数相同时,中位数为两个堆顶元素的平均值
        if len(self.max_heap) == len(self.min_heap):
            return (-self.max_heap[0] + self.min_heap[0]) / 2
        # 否则中位数为堆顶元素较多的那个堆的堆顶
        elif len(self.max_heap) > len(self.min_heap):
            return -self.max_heap[0]
        else:
            return self.min_heap[0]

if __name__ == '__main__':
    median = DynamicMedian()
    nums = [5, 2, 3, 4, 1, 6, 7, 0, 8]
    for num in nums:
        median.insert(num)
        print('当前中位数为:', median.find_median())

输出结果如下:

当前中位数为: 5
当前中位数为: 3.5
当前中位数为: 3
当前中位数为: 3.5
当前中位数为: 3
当前中位数为: 3.5
当前中位数为: 4
当前中位数为: 3.5
当前中位数为: 4

如果需要使用字符串作为范例,可以将nums列表中的数字改为字符串,例如nums = ['pidancode', 'com', 'python', 'heap', 'algorithm']

相关文章