簡體   English   中英

嘗試解決leetcode中的滑動窗口中值問題

[英]Trying to solve sliding window median problem in leetcode

我正在研究 LeetCode 代碼挑戰480。滑動窗口中位數

給定一個整數數組 nums 和一個整數k 有一個大小為k的滑動窗口,它從數組的最左邊移動到最右邊。 您只能在窗口中看到k數字。 每次滑動窗口向右移動一個位置。

返回原始數組中每個窗口的中值數組 實際值在 10 -5以內的答案將被接受。

我提交了我的代碼,但它在測試用例上失敗了。 我懷疑我的這部分代碼有問題:

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;
}

這是完整的代碼:

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;
}

什么地方出了錯:

在問題的第 30 個測試用例(鏈接: https ://leetcode.com/problems/sliding-window-median/submissions/859041571/ )中,它解析為錯誤的答案,但是當我選擇其中一個解析為 a 的窗口時錯誤的答案它給了我一個正確的答案。 我目前感到困惑,因為其中兩個堆相當平衡(因為一個堆不超過一個元素)並且我已經測試了我的堆,兩者似乎都工作得很好。 如果有人幫助我,那將非常有幫助。

鏈接到我關注的 SO 問題:

你的堆實現存在這些問題:

  1. 當給出超出范圍的索引時, get函數將返回null ,這意味着您的sink方法中的while條件有時會選擇一個不存在的孩子(當只有一個孩子時)。 請注意,與null的數值比較會將null視為 0,並且根據您與之比較的值的符號可以給出 false 或 true。

    例如,您的代碼由於這個原因未能通過此測試用例:

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

    您可以通過返回undefined而不是null來解決這個問題。 然后還要確保比較運算符的假邊是帶 +1 的那一邊(選擇左邊的孩子),而真邊取另一個孩子。

  2. 當第二個參數為true時, pop方法不保證恢復堆屬性。 它負責下沉給定索引處的值,但不考慮該值實際上應該冒泡的情況!

    例如,您的代碼由於這個原因未能通過此測試用例:

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

    這是一個簡化的示例,其中我描述了一個具有引用值的最小堆:

     5 / \ 8 6 / \ / 10 12 7

    如果要刪除值為 10 的節點,交換操作將給出這個最小堆(這是正確的):

     5 / \ 8 6 / \ / 7 12 10

    然后您的代碼在該節點上調用值為 7 的sink 。很明顯,這里沒有任何東西可以下沉,而是 7 應該冒泡並與 8 交換。您的代碼必須預見這兩種情況:篩選或冒泡。

如果您在堆實現中解決了這兩個問題,它就會起作用。

我在這里提供您必須進行的文字更改:

  1. get方法中,將return null替換為return undefined (或省略顯式值)

  2. MaxHeap sink方法中,交換比較器表達式,替換:

     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)){

    和:

     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. pop方法中,替換:

     this.sink(index);

    和:

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

    (您也可以先檢查兩者中的哪一個是需要的,但是只調用這兩個方法並沒有什么壞處)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM