{"record":{"id":"678a0357ab86436f","repo":"krahets/hello-algo","slug":"error-678a03","errorCode":null,"errorMessage":"堆为空","messagePattern":"堆为空","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"codes/javascript/chapter_heap/my_heap.js","lineNumber":85,"sourceCode":"\n    /* 从节点 i 开始，从底至顶堆化 */\n    #siftUp(i) {\n        while (true) {\n            // 获取节点 i 的父节点\n            const p = this.#parent(i);\n            // 当“越过根节点”或“节点无须修复”时，结束堆化\n            if (p < 0 || this.#maxHeap[i] <= this.#maxHeap[p]) break;\n            // 交换两节点\n            this.#swap(i, p);\n            // 循环向上堆化\n            i = p;\n        }\n    }\n\n    /* 元素出堆 */\n    pop() {\n        // 判空处理\n        if (this.isEmpty()) throw new Error('堆为空');\n        // 交换根节点与最右叶节点（交换首元素与尾元素）\n        this.#swap(0, this.size() - 1);\n        // 删除节点\n        const val = this.#maxHeap.pop();\n        // 从顶至底堆化\n        this.#siftDown(0);\n        // 返回堆顶元素\n        return val;\n    }\n\n    /* 从节点 i 开始，从顶至底堆化 */\n    #siftDown(i) {\n        while (true) {\n            // 判断节点 i, l, r 中值最大的节点，记为 ma\n            const l = this.#left(i),\n                r = this.#right(i);\n            let ma = i;\n            if (l < this.size() && this.#maxHeap[l] > this.#maxHeap[ma]) ma = l;","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/krahets/hello-algo/blob/69932aed1891a7b7f6a0de88cd116d3fe13e7032/codes/javascript/chapter_heap/my_heap.js#L67-L103","documentation":"Thrown by pop() on a max-heap (MaxHeap) when the heap is empty. pop() swaps the root with the last leaf, removes the last element, then sifts the new root down to restore the heap property; calling it on an empty heap has no element to return, so the guard prevents an invalid swap/undefined return.","triggerScenarios":"Calling pop() more times than elements were pushed; popping a freshly constructed heap; popping after the last element was already removed; popping in a loop without an emptiness guard.","commonSituations":"Drain loops (while(true) heap.pop()); priority-queue consumers pulling more tasks than were enqueued; using the heap as a sorting sink without checking size.","solutions":["Guard every pop: while (!heap.isEmpty()) { const x = heap.pop(); ... }.","Check heap.size() > 0 before a single pop.","Track enqueue/dequeue counts in the caller.","If draining, break the loop on isEmpty() rather than catching the throw."],"exampleFix":"// before\nwhile (true) {\n  const x = heap.pop(); // throws when drained\n}\n// after\nwhile (!heap.isEmpty()) {\n  const x = heap.pop();\n}","handlingStrategy":"validation","validationCode":"function safePop(heap) {\n  if (!heap.isEmpty()) return heap.pop();\n  throw new Error('heap empty');\n}","typeGuard":"function heapHasElements(heap) {\n  return heap.size() > 0;\n}","tryCatchPattern":"try {\n  const x = heap.pop();\n} catch (e) {\n  if (e instanceof Error && e.message === '堆为空') { /* drain complete */ } else throw e;\n}","preventionTips":["Always drain with while (!heap.isEmpty()) heap.pop();.","Track enqueue/dequeue counts in the caller.","For priority queues, signal 'no task' via isEmpty() rather than catching the throw.","Reset/validate heap state at the start of each processing round."],"tags":["heap","priority-queue","empty-state","javascript"],"backgroundTag":null,"analyzedSha":"69932aed1891a7b7f6a0de88cd116d3fe13e7032","analyzedAt":"2026-08-13T23:02:37.581Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}