【路飞】算法与数据结构-面试题 17.20. 连续中值

402 阅读2分钟

不管全世界所有人怎么说,我都认为自己的感受才是正确的。无论别人怎么看,我绝不打乱自己的节奏。喜欢的事自然可以坚持,不喜欢的怎么也长久不了。

LeetCode:原题地址

题目要求

随机产生数字并传递给一个方法。你能否完成这个方法,在每次产生新值时,寻找当前所有值的中间值(中位数)并保存。

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。 double findMedian() - 返回目前所有元素的中位数。

示例 1:

addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3) 
findMedian() -> 2

思路

题意很简单,返回中值,可以很容易联想到使用两个堆,大顶堆存放排序后前一半的数据,小顶堆存放排序后后一半的数据;且满足大顶堆的个数和小顶堆的个数关系是maxSize-minSize===0||maxSize-minSize===1,具体思路如下: 1.编写两个类大顶堆类MaxHeap,小顶堆类MinHeap; 2.addNum时,初始直接调用大顶堆的入堆操作,后续的判断大顶堆的堆顶元素和num的关系,如果num小于大顶堆堆顶元素,则入大顶堆,否则入小顶堆,入堆后,会破坏大小顶堆的个数关系,则进行调整,调整的逻辑是哪个堆个数偏大,则删除这个堆的堆顶,并将删除的数入另一个堆,调整一次即可; 3.findMedian,如果两个堆的个数一致,则分别取两个堆的堆顶并除2即可,否则取大顶堆的堆顶元素即可。

代码

/**
 * initialize your data structure here.
 */
var MedianFinder = function () {
    this.minHeap = new MinHeap();
    this.maxHeap = new MaxHeap();
};

/** 
 * @param {number} num
 * @return {void}
 */
MedianFinder.prototype.addNum = function (num) {
    if (this.maxHeap.isEmpty()) {
        this.maxHeap.push(num)
    } else {
        if (this.maxHeap.top() > num) {
            this.maxHeap.push(num)
        } else {
            this.minHeap.push(num)
        }
        let maxSize = this.maxHeap.getSize();
        let minSize = this.minHeap.getSize();
        if (minSize > maxSize) {
            let top = this.minHeap.pop();
            this.maxHeap.push(top)
        } else if (maxSize - 1 > minSize) {
            let top = this.maxHeap.pop();
            this.minHeap.push(top)
        }
    }
};

/**
 * @return {number}
 */
MedianFinder.prototype.findMedian = function () {
    let maxSize = this.maxHeap.getSize();
    let minSize = this.minHeap.getSize();
    if (maxSize === minSize) {
        return (this.maxHeap.top() + this.minHeap.top()) / 2;
    } else {
        return this.maxHeap.top();
    }
};

class MaxHeap {
    constructor() {
        this.data = [0];
        this.count = 0;
    }

    isEmpty() {
        return this.count === 0;
    }

    push(val) {
        this.count++;
        this.data[this.count] = val;
        let i = this.count;
        while (parseInt(i / 2) > 0 && this.data[i] > this.data[parseInt(i / 2)]) {
            this.swap(i, parseInt(i / 2));
            i = parseInt(i / 2);
        }
    }

    pop() {
        let top = this.data[1]
        if (this.count > 1) {
            this.data[1] = this.data[this.count];
            this.data.splice(this.count, 1);
            this.count--;
            let i = 1;
            while (true) {
                let maxPos = i;
                if (i * 2 <= this.count && this.data[i] < this.data[i * 2]) {
                    maxPos = i * 2;
                }
                if (i * 2 + 1 <= this.count && this.data[maxPos] < this.data[i * 2 + 1]) {
                    maxPos = i * 2 + 1;
                }
                if (maxPos === i) {
                    break;
                }
                this.swap(i, maxPos);
                i = maxPos;
            }
        } else {
            this.count = 0;
            this.data.splice(1, 1);
        }
        return top;
    }

    swap(i, j) {
        let temp = this.data[i];
        this.data[i] = this.data[j];
        this.data[j] = temp;
    }

    getSize() {
        return this.count;
    }

    top() {
        return this.data[1]
    }
}

class MinHeap {
    constructor() {
        this.data = [0];
        this.count = 0;
    }

    push(val) {
        this.count++;
        this.data[this.count] = val;
        let i = this.count;
        while (parseInt(i / 2) > 0 && this.data[i] < this.data[parseInt(i / 2)]) {
            this.swap(i, parseInt(i / 2));
            i = parseInt(i / 2);
        }
    }

    pop() {
        let top = this.data[1]
        if (this.count > 1) {
            this.data[1] = this.data[this.count];
            this.data.splice(this.count, 1);
            this.count--;
            let i = 1;
            while (true) {
                let maxPos = i;
                if (i * 2 <= this.count && this.data[i] > this.data[i * 2]) {
                    maxPos = i * 2;
                }
                if (i * 2 + 1 <= this.count && this.data[maxPos] > this.data[i * 2 + 1]) {
                    maxPos = i * 2 + 1;
                }
                if (maxPos === i) {
                    break;
                }
                this.swap(i, maxPos);
                i = maxPos;
            }
        } else {
            this.count = 0;
            this.data.splice(1, 1);
        }
        return top;
    }

    swap(i, j) {
        let temp = this.data[i];
        this.data[i] = this.data[j];
        this.data[j] = temp;
    }

    getSize() {
        return this.count;
    }

    top() {
        return this.data[1]
    }
}

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