295. 数据流的中位数(难)

题目

  • 中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
    例如 arr = [2,3,4] 的中位数是 3 。
    例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5 。
    实现 MedianFinder 类:
    MedianFinder() 初始化 MedianFinder 对象。
    void addNum(int num) 将数据流中的整数 num 添加到数据结构中。
    double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1:

输入
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
输出
[null, null, null, 1.5, null, 2.0]
解释
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1); // arr = [1]
medianFinder.addNum(2); // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3); // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

题解:排序

  • 超出时间限制,时间复杂度O(nlogn),每次调用 findMedian 都需要排序,造成整体效率低下。
var MedianFinder = function() {
        this.arr = []
};

/** 
 * @param {number} num
 * @return {void}
 */
MedianFinder.prototype.addNum = function(num) {
    this.arr.push(num)
};

/**
 * @return {number}
 */
MedianFinder.prototype.findMedian = function() {
    const n = this.arr.length
    this.arr.sort((a, b) => a - b); // 确保数组是排序的
    if(n%2==0) {//偶数
        return (this.arr[n/2-1]+this.arr[n/2])/2
    }
    else{//奇数
        return this.arr[Math.floor(n/2)]
    }
};

法二、二分

  • 时间复杂度O(n)
var MedianFinder = function() {
    this.arr = []; // 初始化一个空数组
};

/** 
 * 添加数字到数组中
 * @param {number} num - 要添加的数字
 * @return {void}
 */
MedianFinder.prototype.addNum = function(num) {
    // 使用二分查找插入元素,以保持数组的排序
    let left = 0;
    let right = this.arr.length;

    while (left < right) {
        const mid = Math.floor((left + right) / 2);
        if (this.arr[mid] < num) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }

    // 在找到的位置插入元素
    this.arr.splice(left, 0, num);
};

/**
 * 查找当前数组的中位数
 * @return {number} - 中位数
 */
MedianFinder.prototype.findMedian = function() {
    const n = this.arr.length; // 获取数组长度

    if (n % 2 === 0) { // 如果长度为偶数
        return (this.arr[n / 2 - 1] + this.arr[n / 2]) / 2; // 返回中间两个数的平均值
    } else { // 如果长度为奇数
        return this.arr[Math.floor(n / 2)]; // 返回中间的数
    }
};

法三、最大堆和最小堆

  • 时间复杂度O(log n)
var MedianFinder = function() {
    this.maxHeap = []; // 最大堆(存储较小的一半)
    this.minHeap = []; // 最小堆(存储较大的一半)
};

/** 
 * 添加数字到堆中
 * @param {number} num - 要添加的数字
 * @return {void}
 */
MedianFinder.prototype.addNum = function(num) {
    // 将数字添加到最大堆
    this.maxHeap.push(num);
    this.maxHeap.sort((a, b) => b - a); // 最大堆需要降序排列

    // 确保最大堆的最大元素小于或等于最小堆的最小元素
    if (this.minHeap.length > 0 && this.maxHeap[0] > this.minHeap[0]) {
        const maxToMin = this.maxHeap.shift(); // 从最大堆移除最大元素
        this.minHeap.push(maxToMin);
        this.minHeap.sort((a, b) => a - b); // 最小堆需要升序排列
    }

    // 重新平衡堆的大小
    if (this.maxHeap.length > this.minHeap.length + 1) {
        const moveToMin = this.maxHeap.shift();
        this.minHeap.push(moveToMin);
        this.minHeap.sort((a, b) => a - b);
    } else if (this.minHeap.length > this.maxHeap.length) {
        const moveToMax = this.minHeap.shift();
        this.maxHeap.push(moveToMax);
        this.maxHeap.sort((a, b) => b - a);
    }
};

/**
 * 查找当前数组的中位数
 * @return {number} - 中位数
 */
MedianFinder.prototype.findMedian = function() {
    if (this.maxHeap.length > this.minHeap.length) {
        return this.maxHeap[0]; // 最大堆的顶端是中位数
    } else {
        return (this.maxHeap[0] + this.minHeap[0]) / 2; // 两个堆的顶端的平均值
    }
};
posted @ 2025-03-16 09:40  Frommoon  阅读(24)  评论(0)    收藏  举报