简体   繁体   中英

Trying to solve sliding window median problem in leetcode

I working on LeetCode code challenge 480. Sliding Window Median :

You are given an integer array nums and an integer k . There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.

Return the median array for each window in the original array . Answers within 10 -5 of the actual value will be accepted.

I submitted my code, but it fails on test cases. I suspect that there is a problem in this part of my code:

const medianSlidingWindow = (array, window) => {
    let start = 0;
    let end = window - 1;
    const min = new MinHeap(array);
    const max = new MaxHeap(array);
    const insert = (index) => {
        if(max.size === 0){
            max.push(index);
            return;
        }
        (array[index] >= max.peak) ? min.push(index) : max.push(index);
        balance();
    }
    const balance = () => {
        if(Math.abs(max.size - min.size) >= 2){
            const returned = (max.size > min.size) ? max.pop() : min.pop();
            (max.size > min.size) ? min.push(returned) : max.push(returned);
        }
    }
    const remove = (index) => {
        (max.has(index)) ? max.pop(index, true) : min.pop(index, true);
        balance();
    }
    const next = () => {
        remove(start++);
        insert(++end);
    }
    const getMedian = () => {
        if(window % 2 === 0) return (max.peak + min.peak)/2;
        return (max.size > min.size) ? max.peak : min.peak;
    }
    for(let i = 0; i <= end; i++){
        insert(i);
    }
    const ret = [];
    while(end < array.length){
        ret.push(getMedian());
        next();
    }
    return ret;
}

Here is the full code:

class MaxHeap{
    #array = [];
    #size = 0;
    #reference = [];
    #map = new Map();
    constructor(reference = []){
        this.#reference = reference;
    }
    get size(){
        return this.#size;
    }
    /* Debug */
    get array(){
        return this.#array;
    }
    get peak(){
        return this.get(0);
    }
    get(index){
        if(index === null || index < 0 || index >= this.#array.length) return null;
        return this.#reference[this.#array[index]];
    }
    has(indexReference){
        return this.#map.has(indexReference);
    }
    swap(indexA, indexB){
        let temp = this.#map.get(this.#array[indexA]);
        this.#map.set(this.#array[indexA], indexB);
        this.#map.set(this.#array[indexB], temp);
        [this.#array[indexA], this.#array[indexB]] = [this.#array[indexB], this.#array[indexA]];
        
    }
    sink(index){
        let currentIndex = index;
        let greterChild;
        while((this.get(greterChild = this.get(2*currentIndex+1) >= this.get(2*currentIndex + 2) ? 2*currentIndex + 1 : 2*currentIndex + 2) ?? Number.MIN_SAFE_INTEGER) > this.get(currentIndex)){
            this.swap(currentIndex, greterChild);
            currentIndex = greterChild;
        }
    }
    bubble(index){
        let currentIndex = index;
        let parent;
        while((this.get(parent = Math.ceil((currentIndex - 2)/2)) ?? Number.MAX_SAFE_INTEGER) < this.get(currentIndex)){
            this.swap(currentIndex, parent);
            currentIndex = parent;
        }
    }
    push(...char){
        if(char[0].constructor === Array) char = char.flat();
        for(let i = 0; i < char.length; i++){
            this.#array.push(char[i]);
            this.#map.set(char[i], this.#array.length - 1)
            this.bubble(this.#array.length - 1);
            this.#size++;
        }
    }
    pop(index = 0, fromReference = false){
        const ret = (fromReference) ? index :this.#array[index];
        if(fromReference) index = this.#map.get(index);
        this.swap(index, this.#array.length - 1);
        this.#map.delete(ret);
        this.#array.pop();
        this.sink(index);
        this.#size--;
        return ret;
    }
}
class MinHeap extends MaxHeap{
    constructor(reference = []){
        super(reference);
    }
    get size(){
        return super.size;
    }
    get peak(){
        return super.peak;
    }
    /* Debug */
    get array(){
        return super.array;
    }
    bubble(index){
        let currentIndex = index;
        let parent;
        while((this.get(parent = Math.ceil((currentIndex - 2)/2)) ?? Number.MIN_SAFE_INTEGER) > this.get(currentIndex)){
            this.swap(currentIndex, parent);
            currentIndex = parent;
        }
    }
    sink(index){
        let currentIndex = index;
        let lesserChild;
        while((this.get(lesserChild = this.get(2*currentIndex+1) >= this.get(2*currentIndex + 2) ? 2*currentIndex + 2 : 2*currentIndex + 1) ?? Number.MAX_SAFE_INTEGER) < this.get(currentIndex)){
            this.swap(currentIndex, lesserChild);
            currentIndex = lesserChild;
        }
    }
}

const medianSlidingWindow = (array, window) => {
    let start = 0;
    let end = window - 1;
    const min = new MinHeap(array);
    const max = new MaxHeap(array);
    const insert = (index) => {
        if(max.size === 0){
            max.push(index);
            return;
        }
        (array[index] >= max.peak) ? min.push(index) : max.push(index);
        balance();
    }
    const balance = () => {
        if(Math.abs(max.size - min.size) >= 2){
            const returned = (max.size > min.size) ? max.pop() : min.pop();
            (max.size > min.size) ? min.push(returned) : max.push(returned);
        }
    }
    const remove = (index) => {
        (max.has(index)) ? max.pop(index, true) : min.pop(index, true);
        balance();
    }
    const next = () => {
        remove(start++);
        insert(++end);
    }
    const getMedian = () => {
        if(window % 2 === 0) return (max.peak + min.peak)/2;
        return (max.size > min.size) ? max.peak : min.peak;
    }
    for(let i = 0; i <= end; i++){
        insert(i);
    }
    const ret = [];
    while(end < array.length){
        ret.push(getMedian());
        next();
    }
    return ret;
}

What went wrong:

On the 30th testcase of the problem (link: https://leetcode.com/problems/sliding-window-median/submissions/859041571/ ), it resolves to a wrong answer but when I pick one of the windows that resolves to a wrong answer it gives me a correct answer. I'm currently confused because two of the heaps are fairly balanced (as one heap doesn't exceed above one element) and I've tested my heap that both seem to work perfectly. It will be very helpful if somebody helps me.

Link to SO questions I've followed:

There are these problems in your heap implementation:

  1. The get function will return null when an out of range index is given, which means the while condition in your sink method could sometimes choose an non-existing child (when there is only one child). Note that a numerical comparison with null will treat that null as 0, and depending of the sign of the value you compare it with can give false or true.

    For example, your code fails this test case for that reason:

     nums=[1,2,3,4] k=4

    You can fix this by returning undefined instead of null . Then also make sure that the false side of the comparison operator is the one with +1 (choosing the left child), while the true side takes the other child.

  2. The pop method, when called with true for the second argument, does not guarantee to restore the heap property. It takes care of sinking the value at the given index, but does not consider the case where this value should actually bubble up !

    For example, your code fails this test case for that reason:

     nums=[10,6,5,2,3,0,8,1,4,12,7,13,11,9] k=11

    Here is a simplified example where I depict a min-heap with the referenced values:

     5 / \ 8 6 / \ / 10 12 7

    If the node with value 10 is to be removed, the swap action will give this min-heap (which is correct):

     5 / \ 8 6 / \ / 7 12 10

    And then your code calls sink on that node with value 7. It is clear that there is nothing to sink here, but instead that 7 should bubble up and swap with 8. Your code must foresee both scenarios: sift or bubble.

If you fix those two issues in your heap implementation, it will work.

I provide here the literal changes you have to make:

  1. In the get method, replace return null with return undefined (or omit the explicit value)

  2. In the MaxHeap sink method, swap the comparator expression, replacing:

     while((this.get(greterChild = this.get(2*currentIndex+1) >= this.get(2*currentIndex + 2)? 2*currentIndex + 1: 2*currentIndex + 2)?? Number.MIN_SAFE_INTEGER) > this.get(currentIndex)){

    with:

     while((this.get(greterChild = this.get(2*currentIndex+1) <= this.get(2*currentIndex + 2)? 2*currentIndex + 2: 2*currentIndex + 1)?? Number.MIN_SAFE_INTEGER) > this.get(currentIndex)){
  3. In the pop method, replace:

     this.sink(index);

    with:

     this.sink(index); this.bubble(index);

    (You can also first check which of both is needed, but it doesn't hurt to just call both methods)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM