--- comments: true --- # 8.1 Heap A "heap" is a complete binary tree that satisfies specific conditions and can be mainly divided into two types, as shown in the Figure 8-1 . - "Min heap": The value of any node $\leq$ the values of its child nodes. - "Max heap": The value of any node $\geq$ the values of its child nodes. ![Min heap and max heap](heap.assets/min_heap_and_max_heap.png){ class="animation-figure" }
Figure 8-1 Min heap and max heap
As a special case of a complete binary tree, heaps have the following characteristics: - The bottom layer nodes are filled from left to right, and nodes in other layers are fully filled. - The root node of the binary tree is called the "heap top," and the bottom-rightmost node is called the "heap bottom." - For max heaps (min heaps), the value of the heap top element (root node) is the largest (smallest). ## 8.1.1 Common operations on heaps It should be noted that many programming languages provide a "priority queue," which is an abstract data structure defined as a queue with priority sorting. In fact, **heaps are often used to implement priority queues, with max heaps equivalent to priority queues where elements are dequeued in descending order**. From a usage perspective, we can consider "priority queue" and "heap" as equivalent data structures. Therefore, this book does not make a special distinction between the two, uniformly referring to them as "heap." Common operations on heaps are shown in the Table 8-1 , and the method names depend on the programming language.Table 8-1 Efficiency of Heap Operations
Figure 8-2 Representation and storage of heaps
We can encapsulate the index mapping formula into functions for convenient later use: === "Python" ```python title="my_heap.py" def left(self, i: int) -> int: """获取左子节点的索引""" return 2 * i + 1 def right(self, i: int) -> int: """获取右子节点的索引""" return 2 * i + 2 def parent(self, i: int) -> int: """获取父节点的索引""" return (i - 1) // 2 # 向下整除 ``` === "C++" ```cpp title="my_heap.cpp" /* 获取左子节点的索引 */ int left(int i) { return 2 * i + 1; } /* 获取右子节点的索引 */ int right(int i) { return 2 * i + 2; } /* 获取父节点的索引 */ int parent(int i) { return (i - 1) / 2; // 向下整除 } ``` === "Java" ```java title="my_heap.java" /* 获取左子节点的索引 */ int left(int i) { return 2 * i + 1; } /* 获取右子节点的索引 */ int right(int i) { return 2 * i + 2; } /* 获取父节点的索引 */ int parent(int i) { return (i - 1) / 2; // 向下整除 } ``` === "C#" ```csharp title="my_heap.cs" /* 获取左子节点的索引 */ int Left(int i) { return 2 * i + 1; } /* 获取右子节点的索引 */ int Right(int i) { return 2 * i + 2; } /* 获取父节点的索引 */ int Parent(int i) { return (i - 1) / 2; // 向下整除 } ``` === "Go" ```go title="my_heap.go" /* 获取左子节点的索引 */ func (h *maxHeap) left(i int) int { return 2*i + 1 } /* 获取右子节点的索引 */ func (h *maxHeap) right(i int) int { return 2*i + 2 } /* 获取父节点的索引 */ func (h *maxHeap) parent(i int) int { // 向下整除 return (i - 1) / 2 } ``` === "Swift" ```swift title="my_heap.swift" /* 获取左子节点的索引 */ func left(i: Int) -> Int { 2 * i + 1 } /* 获取右子节点的索引 */ func right(i: Int) -> Int { 2 * i + 2 } /* 获取父节点的索引 */ func parent(i: Int) -> Int { (i - 1) / 2 // 向下整除 } ``` === "JS" ```javascript title="my_heap.js" /* 获取左子节点的索引 */ #left(i) { return 2 * i + 1; } /* 获取右子节点的索引 */ #right(i) { return 2 * i + 2; } /* 获取父节点的索引 */ #parent(i) { return Math.floor((i - 1) / 2); // 向下整除 } ``` === "TS" ```typescript title="my_heap.ts" /* 获取左子节点的索引 */ left(i: number): number { return 2 * i + 1; } /* 获取右子节点的索引 */ right(i: number): number { return 2 * i + 2; } /* 获取父节点的索引 */ parent(i: number): number { return Math.floor((i - 1) / 2); // 向下整除 } ``` === "Dart" ```dart title="my_heap.dart" /* 获取左子节点的索引 */ int _left(int i) { return 2 * i + 1; } /* 获取右子节点的索引 */ int _right(int i) { return 2 * i + 2; } /* 获取父节点的索引 */ int _parent(int i) { return (i - 1) ~/ 2; // 向下整除 } ``` === "Rust" ```rust title="my_heap.rs" /* 获取左子节点的索引 */ fn left(i: usize) -> usize { 2 * i + 1 } /* 获取右子节点的索引 */ fn right(i: usize) -> usize { 2 * i + 2 } /* 获取父节点的索引 */ fn parent(i: usize) -> usize { (i - 1) / 2 // 向下整除 } ``` === "C" ```c title="my_heap.c" /* 获取左子节点的索引 */ int left(MaxHeap *maxHeap, int i) { return 2 * i + 1; } /* 获取右子节点的索引 */ int right(MaxHeap *maxHeap, int i) { return 2 * i + 2; } /* 获取父节点的索引 */ int parent(MaxHeap *maxHeap, int i) { return (i - 1) / 2; // 向下取整 } ``` === "Kotlin" ```kotlin title="my_heap.kt" /* 获取左子节点的索引 */ fun left(i: Int): Int { return 2 * i + 1 } /* 获取右子节点的索引 */ fun right(i: Int): Int { return 2 * i + 2 } /* 获取父节点的索引 */ fun parent(i: Int): Int { return (i - 1) / 2 // 向下整除 } ``` === "Ruby" ```ruby title="my_heap.rb" [class]{MaxHeap}-[func]{left} [class]{MaxHeap}-[func]{right} [class]{MaxHeap}-[func]{parent} ``` === "Zig" ```zig title="my_heap.zig" // 获取左子节点的索引 fn left(i: usize) usize { return 2 * i + 1; } // 获取右子节点的索引 fn right(i: usize) usize { return 2 * i + 2; } // 获取父节点的索引 fn parent(i: usize) usize { // return (i - 1) / 2; // 向下整除 return @divFloor(i - 1, 2); } ``` ### 2. Accessing the top element of the heap The top element of the heap is the root node of the binary tree, which is also the first element of the list: === "Python" ```python title="my_heap.py" def peek(self) -> int: """访问堆顶元素""" return self.max_heap[0] ``` === "C++" ```cpp title="my_heap.cpp" /* 访问堆顶元素 */ int peek() { return maxHeap[0]; } ``` === "Java" ```java title="my_heap.java" /* 访问堆顶元素 */ int peek() { return maxHeap.get(0); } ``` === "C#" ```csharp title="my_heap.cs" /* 访问堆顶元素 */ int Peek() { return maxHeap[0]; } ``` === "Go" ```go title="my_heap.go" /* 访问堆顶元素 */ func (h *maxHeap) peek() any { return h.data[0] } ``` === "Swift" ```swift title="my_heap.swift" /* 访问堆顶元素 */ func peek() -> Int { maxHeap[0] } ``` === "JS" ```javascript title="my_heap.js" /* 访问堆顶元素 */ peek() { return this.#maxHeap[0]; } ``` === "TS" ```typescript title="my_heap.ts" /* 访问堆顶元素 */ peek(): number { return this.maxHeap[0]; } ``` === "Dart" ```dart title="my_heap.dart" /* 访问堆顶元素 */ int peek() { return _maxHeap[0]; } ``` === "Rust" ```rust title="my_heap.rs" /* 访问堆顶元素 */ fn peek(&self) -> OptionFigure 8-3 Steps of element insertion into the heap
Given a total of $n$ nodes, the height of the tree is $O(\log n)$. Hence, the loop iterations for the heapify operation are at most $O(\log n)$, **making the time complexity of the element insertion operation $O(\log n)$**. The code is as shown: === "Python" ```python title="my_heap.py" def push(self, val: int): """元素入堆""" # 添加节点 self.max_heap.append(val) # 从底至顶堆化 self.sift_up(self.size() - 1) def sift_up(self, i: int): """从节点 i 开始,从底至顶堆化""" while True: # 获取节点 i 的父节点 p = self.parent(i) # 当“越过根节点”或“节点无须修复”时,结束堆化 if p < 0 or self.max_heap[i] <= self.max_heap[p]: break # 交换两节点 self.swap(i, p) # 循环向上堆化 i = p ``` === "C++" ```cpp title="my_heap.cpp" /* 元素入堆 */ void push(int val) { // 添加节点 maxHeap.push_back(val); // 从底至顶堆化 siftUp(size() - 1); } /* 从节点 i 开始,从底至顶堆化 */ void siftUp(int i) { while (true) { // 获取节点 i 的父节点 int p = parent(i); // 当“越过根节点”或“节点无须修复”时,结束堆化 if (p < 0 || maxHeap[i] <= maxHeap[p]) break; // 交换两节点 swap(maxHeap[i], maxHeap[p]); // 循环向上堆化 i = p; } } ``` === "Java" ```java title="my_heap.java" /* 元素入堆 */ void push(int val) { // 添加节点 maxHeap.add(val); // 从底至顶堆化 siftUp(size() - 1); } /* 从节点 i 开始,从底至顶堆化 */ void siftUp(int i) { while (true) { // 获取节点 i 的父节点 int p = parent(i); // 当“越过根节点”或“节点无须修复”时,结束堆化 if (p < 0 || maxHeap.get(i) <= maxHeap.get(p)) break; // 交换两节点 swap(i, p); // 循环向上堆化 i = p; } } ``` === "C#" ```csharp title="my_heap.cs" /* 元素入堆 */ void Push(int val) { // 添加节点 maxHeap.Add(val); // 从底至顶堆化 SiftUp(Size() - 1); } /* 从节点 i 开始,从底至顶堆化 */ void SiftUp(int i) { while (true) { // 获取节点 i 的父节点 int p = Parent(i); // 若“越过根节点”或“节点无须修复”,则结束堆化 if (p < 0 || maxHeap[i] <= maxHeap[p]) break; // 交换两节点 Swap(i, p); // 循环向上堆化 i = p; } } ``` === "Go" ```go title="my_heap.go" /* 元素入堆 */ func (h *maxHeap) push(val any) { // 添加节点 h.data = append(h.data, val) // 从底至顶堆化 h.siftUp(len(h.data) - 1) } /* 从节点 i 开始,从底至顶堆化 */ func (h *maxHeap) siftUp(i int) { for true { // 获取节点 i 的父节点 p := h.parent(i) // 当“越过根节点”或“节点无须修复”时,结束堆化 if p < 0 || h.data[i].(int) <= h.data[p].(int) { break } // 交换两节点 h.swap(i, p) // 循环向上堆化 i = p } } ``` === "Swift" ```swift title="my_heap.swift" /* 元素入堆 */ func push(val: Int) { // 添加节点 maxHeap.append(val) // 从底至顶堆化 siftUp(i: size() - 1) } /* 从节点 i 开始,从底至顶堆化 */ func siftUp(i: Int) { var i = i while true { // 获取节点 i 的父节点 let p = parent(i: i) // 当“越过根节点”或“节点无须修复”时,结束堆化 if p < 0 || maxHeap[i] <= maxHeap[p] { break } // 交换两节点 swap(i: i, j: p) // 循环向上堆化 i = p } } ``` === "JS" ```javascript title="my_heap.js" /* 元素入堆 */ push(val) { // 添加节点 this.#maxHeap.push(val); // 从底至顶堆化 this.#siftUp(this.size() - 1); } /* 从节点 i 开始,从底至顶堆化 */ #siftUp(i) { while (true) { // 获取节点 i 的父节点 const p = this.#parent(i); // 当“越过根节点”或“节点无须修复”时,结束堆化 if (p < 0 || this.#maxHeap[i] <= this.#maxHeap[p]) break; // 交换两节点 this.#swap(i, p); // 循环向上堆化 i = p; } } ``` === "TS" ```typescript title="my_heap.ts" /* 元素入堆 */ push(val: number): void { // 添加节点 this.maxHeap.push(val); // 从底至顶堆化 this.siftUp(this.size() - 1); } /* 从节点 i 开始,从底至顶堆化 */ siftUp(i: number): void { while (true) { // 获取节点 i 的父节点 const p = this.parent(i); // 当“越过根节点”或“节点无须修复”时,结束堆化 if (p < 0 || this.maxHeap[i] <= this.maxHeap[p]) break; // 交换两节点 this.swap(i, p); // 循环向上堆化 i = p; } } ``` === "Dart" ```dart title="my_heap.dart" /* 元素入堆 */ void push(int val) { // 添加节点 _maxHeap.add(val); // 从底至顶堆化 siftUp(size() - 1); } /* 从节点 i 开始,从底至顶堆化 */ void siftUp(int i) { while (true) { // 获取节点 i 的父节点 int p = _parent(i); // 当“越过根节点”或“节点无须修复”时,结束堆化 if (p < 0 || _maxHeap[i] <= _maxHeap[p]) { break; } // 交换两节点 _swap(i, p); // 循环向上堆化 i = p; } } ``` === "Rust" ```rust title="my_heap.rs" /* 元素入堆 */ fn push(&mut self, val: i32) { // 添加节点 self.max_heap.push(val); // 从底至顶堆化 self.sift_up(self.size() - 1); } /* 从节点 i 开始,从底至顶堆化 */ fn sift_up(&mut self, mut i: usize) { loop { // 节点 i 已经是堆顶节点了,结束堆化 if i == 0 { break; } // 获取节点 i 的父节点 let p = Self::parent(i); // 当“节点无须修复”时,结束堆化 if self.max_heap[i] <= self.max_heap[p] { break; } // 交换两节点 self.swap(i, p); // 循环向上堆化 i = p; } } ``` === "C" ```c title="my_heap.c" /* 元素入堆 */ void push(MaxHeap *maxHeap, int val) { // 默认情况下,不应该添加这么多节点 if (maxHeap->size == MAX_SIZE) { printf("heap is full!"); return; } // 添加节点 maxHeap->data[maxHeap->size] = val; maxHeap->size++; // 从底至顶堆化 siftUp(maxHeap, maxHeap->size - 1); } /* 从节点 i 开始,从底至顶堆化 */ void siftUp(MaxHeap *maxHeap, int i) { while (true) { // 获取节点 i 的父节点 int p = parent(maxHeap, i); // 当“越过根节点”或“节点无须修复”时,结束堆化 if (p < 0 || maxHeap->data[i] <= maxHeap->data[p]) { break; } // 交换两节点 swap(maxHeap, i, p); // 循环向上堆化 i = p; } } ``` === "Kotlin" ```kotlin title="my_heap.kt" /* 元素入堆 */ fun push(_val: Int) { // 添加节点 maxHeap.add(_val) // 从底至顶堆化 siftUp(size() - 1) } /* 从节点 i 开始,从底至顶堆化 */ fun siftUp(it: Int) { // Kotlin的函数参数不可变,因此创建临时变量 var i = it while (true) { // 获取节点 i 的父节点 val p = parent(i) // 当“越过根节点”或“节点无须修复”时,结束堆化 if (p < 0 || maxHeap[i] <= maxHeap[p]) break // 交换两节点 swap(i, p) // 循环向上堆化 i = p } } ``` === "Ruby" ```ruby title="my_heap.rb" [class]{MaxHeap}-[func]{push} [class]{MaxHeap}-[func]{sift_up} ``` === "Zig" ```zig title="my_heap.zig" // 元素入堆 fn push(self: *Self, val: T) !void { // 添加节点 try self.max_heap.?.append(val); // 从底至顶堆化 try self.siftUp(self.size() - 1); } // 从节点 i 开始,从底至顶堆化 fn siftUp(self: *Self, i_: usize) !void { var i = i_; while (true) { // 获取节点 i 的父节点 var p = parent(i); // 当“越过根节点”或“节点无须修复”时,结束堆化 if (p < 0 or self.max_heap.?.items[i] <= self.max_heap.?.items[p]) break; // 交换两节点 try self.swap(i, p); // 循环向上堆化 i = p; } } ``` ??? pythontutor "Code Visualization" ### 4. Removing the top element from the heap The top element of the heap is the root node of the binary tree, that is, the first element of the list. If we directly remove the first element from the list, all node indexes in the binary tree would change, making it difficult to use heapify for repairs subsequently. To minimize changes in element indexes, we use the following steps. 1. Swap the top element with the bottom element of the heap (swap the root node with the rightmost leaf node). 2. After swapping, remove the bottom of the heap from the list (note, since it has been swapped, what is actually being removed is the original top element). 3. Starting from the root node, **perform heapify from top to bottom**. As shown in the Figure 8-4 , **the direction of "heapify from top to bottom" is opposite to "heapify from bottom to top"**. We compare the value of the root node with its two children and swap it with the largest child. Then repeat this operation until passing the leaf node or encountering a node that does not need to be swapped. === "<1>" ![Steps of removing the top element from the heap](heap.assets/heap_pop_step1.png){ class="animation-figure" } === "<2>" ![heap_pop_step2](heap.assets/heap_pop_step2.png){ class="animation-figure" } === "<3>" ![heap_pop_step3](heap.assets/heap_pop_step3.png){ class="animation-figure" } === "<4>" ![heap_pop_step4](heap.assets/heap_pop_step4.png){ class="animation-figure" } === "<5>" ![heap_pop_step5](heap.assets/heap_pop_step5.png){ class="animation-figure" } === "<6>" ![heap_pop_step6](heap.assets/heap_pop_step6.png){ class="animation-figure" } === "<7>" ![heap_pop_step7](heap.assets/heap_pop_step7.png){ class="animation-figure" } === "<8>" ![heap_pop_step8](heap.assets/heap_pop_step8.png){ class="animation-figure" } === "<9>" ![heap_pop_step9](heap.assets/heap_pop_step9.png){ class="animation-figure" } === "<10>" ![heap_pop_step10](heap.assets/heap_pop_step10.png){ class="animation-figure" }Figure 8-4 Steps of removing the top element from the heap
Similar to the element insertion operation, the time complexity of the top element removal operation is also $O(\log n)$. The code is as follows: === "Python" ```python title="my_heap.py" def pop(self) -> int: """元素出堆""" # 判空处理 if self.is_empty(): raise IndexError("堆为空") # 交换根节点与最右叶节点(交换首元素与尾元素) self.swap(0, self.size() - 1) # 删除节点 val = self.max_heap.pop() # 从顶至底堆化 self.sift_down(0) # 返回堆顶元素 return val def sift_down(self, i: int): """从节点 i 开始,从顶至底堆化""" while True: # 判断节点 i, l, r 中值最大的节点,记为 ma l, r, ma = self.left(i), self.right(i), i if l < self.size() and self.max_heap[l] > self.max_heap[ma]: ma = l if r < self.size() and self.max_heap[r] > self.max_heap[ma]: ma = r # 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 if ma == i: break # 交换两节点 self.swap(i, ma) # 循环向下堆化 i = ma ``` === "C++" ```cpp title="my_heap.cpp" /* 元素出堆 */ void pop() { // 判空处理 if (isEmpty()) { throw out_of_range("堆为空"); } // 交换根节点与最右叶节点(交换首元素与尾元素) swap(maxHeap[0], maxHeap[size() - 1]); // 删除节点 maxHeap.pop_back(); // 从顶至底堆化 siftDown(0); } /* 从节点 i 开始,从顶至底堆化 */ void siftDown(int i) { while (true) { // 判断节点 i, l, r 中值最大的节点,记为 ma int l = left(i), r = right(i), ma = i; if (l < size() && maxHeap[l] > maxHeap[ma]) ma = l; if (r < size() && maxHeap[r] > maxHeap[ma]) ma = r; // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 if (ma == i) break; swap(maxHeap[i], maxHeap[ma]); // 循环向下堆化 i = ma; } } ``` === "Java" ```java title="my_heap.java" /* 元素出堆 */ int pop() { // 判空处理 if (isEmpty()) throw new IndexOutOfBoundsException(); // 交换根节点与最右叶节点(交换首元素与尾元素) swap(0, size() - 1); // 删除节点 int val = maxHeap.remove(size() - 1); // 从顶至底堆化 siftDown(0); // 返回堆顶元素 return val; } /* 从节点 i 开始,从顶至底堆化 */ void siftDown(int i) { while (true) { // 判断节点 i, l, r 中值最大的节点,记为 ma int l = left(i), r = right(i), ma = i; if (l < size() && maxHeap.get(l) > maxHeap.get(ma)) ma = l; if (r < size() && maxHeap.get(r) > maxHeap.get(ma)) ma = r; // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 if (ma == i) break; // 交换两节点 swap(i, ma); // 循环向下堆化 i = ma; } } ``` === "C#" ```csharp title="my_heap.cs" /* 元素出堆 */ int Pop() { // 判空处理 if (IsEmpty()) throw new IndexOutOfRangeException(); // 交换根节点与最右叶节点(交换首元素与尾元素) Swap(0, Size() - 1); // 删除节点 int val = maxHeap.Last(); maxHeap.RemoveAt(Size() - 1); // 从顶至底堆化 SiftDown(0); // 返回堆顶元素 return val; } /* 从节点 i 开始,从顶至底堆化 */ void SiftDown(int i) { while (true) { // 判断节点 i, l, r 中值最大的节点,记为 ma int l = Left(i), r = Right(i), ma = i; if (l < Size() && maxHeap[l] > maxHeap[ma]) ma = l; if (r < Size() && maxHeap[r] > maxHeap[ma]) ma = r; // 若“节点 i 最大”或“越过叶节点”,则结束堆化 if (ma == i) break; // 交换两节点 Swap(i, ma); // 循环向下堆化 i = ma; } } ``` === "Go" ```go title="my_heap.go" /* 元素出堆 */ func (h *maxHeap) pop() any { // 判空处理 if h.isEmpty() { fmt.Println("error") return nil } // 交换根节点与最右叶节点(交换首元素与尾元素) h.swap(0, h.size()-1) // 删除节点 val := h.data[len(h.data)-1] h.data = h.data[:len(h.data)-1] // 从顶至底堆化 h.siftDown(0) // 返回堆顶元素 return val } /* 从节点 i 开始,从顶至底堆化 */ func (h *maxHeap) siftDown(i int) { for true { // 判断节点 i, l, r 中值最大的节点,记为 max l, r, max := h.left(i), h.right(i), i if l < h.size() && h.data[l].(int) > h.data[max].(int) { max = l } if r < h.size() && h.data[r].(int) > h.data[max].(int) { max = r } // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 if max == i { break } // 交换两节点 h.swap(i, max) // 循环向下堆化 i = max } } ``` === "Swift" ```swift title="my_heap.swift" /* 元素出堆 */ func pop() -> Int { // 判空处理 if isEmpty() { fatalError("堆为空") } // 交换根节点与最右叶节点(交换首元素与尾元素) swap(i: 0, j: size() - 1) // 删除节点 let val = maxHeap.remove(at: size() - 1) // 从顶至底堆化 siftDown(i: 0) // 返回堆顶元素 return val } /* 从节点 i 开始,从顶至底堆化 */ func siftDown(i: Int) { var i = i while true { // 判断节点 i, l, r 中值最大的节点,记为 ma let l = left(i: i) let r = right(i: i) var ma = i if l < size(), maxHeap[l] > maxHeap[ma] { ma = l } if r < size(), maxHeap[r] > maxHeap[ma] { ma = r } // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 if ma == i { break } // 交换两节点 swap(i: i, j: ma) // 循环向下堆化 i = ma } } ``` === "JS" ```javascript title="my_heap.js" /* 元素出堆 */ pop() { // 判空处理 if (this.isEmpty()) throw new Error('堆为空'); // 交换根节点与最右叶节点(交换首元素与尾元素) this.#swap(0, this.size() - 1); // 删除节点 const val = this.#maxHeap.pop(); // 从顶至底堆化 this.#siftDown(0); // 返回堆顶元素 return val; } /* 从节点 i 开始,从顶至底堆化 */ #siftDown(i) { while (true) { // 判断节点 i, l, r 中值最大的节点,记为 ma const l = this.#left(i), r = this.#right(i); let ma = i; if (l < this.size() && this.#maxHeap[l] > this.#maxHeap[ma]) ma = l; if (r < this.size() && this.#maxHeap[r] > this.#maxHeap[ma]) ma = r; // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 if (ma === i) break; // 交换两节点 this.#swap(i, ma); // 循环向下堆化 i = ma; } } ``` === "TS" ```typescript title="my_heap.ts" /* 元素出堆 */ pop(): number { // 判空处理 if (this.isEmpty()) throw new RangeError('Heap is empty.'); // 交换根节点与最右叶节点(交换首元素与尾元素) this.swap(0, this.size() - 1); // 删除节点 const val = this.maxHeap.pop(); // 从顶至底堆化 this.siftDown(0); // 返回堆顶元素 return val; } /* 从节点 i 开始,从顶至底堆化 */ siftDown(i: number): void { while (true) { // 判断节点 i, l, r 中值最大的节点,记为 ma const l = this.left(i), r = this.right(i); let ma = i; if (l < this.size() && this.maxHeap[l] > this.maxHeap[ma]) ma = l; if (r < this.size() && this.maxHeap[r] > this.maxHeap[ma]) ma = r; // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 if (ma === i) break; // 交换两节点 this.swap(i, ma); // 循环向下堆化 i = ma; } } ``` === "Dart" ```dart title="my_heap.dart" /* 元素出堆 */ int pop() { // 判空处理 if (isEmpty()) throw Exception('堆为空'); // 交换根节点与最右叶节点(交换首元素与尾元素) _swap(0, size() - 1); // 删除节点 int val = _maxHeap.removeLast(); // 从顶至底堆化 siftDown(0); // 返回堆顶元素 return val; } /* 从节点 i 开始,从顶至底堆化 */ void siftDown(int i) { while (true) { // 判断节点 i, l, r 中值最大的节点,记为 ma int l = _left(i); int r = _right(i); int ma = i; if (l < size() && _maxHeap[l] > _maxHeap[ma]) ma = l; if (r < size() && _maxHeap[r] > _maxHeap[ma]) ma = r; // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 if (ma == i) break; // 交换两节点 _swap(i, ma); // 循环向下堆化 i = ma; } } ``` === "Rust" ```rust title="my_heap.rs" /* 元素出堆 */ fn pop(&mut self) -> i32 { // 判空处理 if self.is_empty() { panic!("index out of bounds"); } // 交换根节点与最右叶节点(交换首元素与尾元素) self.swap(0, self.size() - 1); // 删除节点 let val = self.max_heap.remove(self.size() - 1); // 从顶至底堆化 self.sift_down(0); // 返回堆顶元素 val } /* 从节点 i 开始,从顶至底堆化 */ fn sift_down(&mut self, mut i: usize) { loop { // 判断节点 i, l, r 中值最大的节点,记为 ma let (l, r, mut ma) = (Self::left(i), Self::right(i), i); if l < self.size() && self.max_heap[l] > self.max_heap[ma] { ma = l; } if r < self.size() && self.max_heap[r] > self.max_heap[ma] { ma = r; } // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 if ma == i { break; } // 交换两节点 self.swap(i, ma); // 循环向下堆化 i = ma; } } ``` === "C" ```c title="my_heap.c" /* 元素出堆 */ int pop(MaxHeap *maxHeap) { // 判空处理 if (isEmpty(maxHeap)) { printf("heap is empty!"); return INT_MAX; } // 交换根节点与最右叶节点(交换首元素与尾元素) swap(maxHeap, 0, size(maxHeap) - 1); // 删除节点 int val = maxHeap->data[maxHeap->size - 1]; maxHeap->size--; // 从顶至底堆化 siftDown(maxHeap, 0); // 返回堆顶元素 return val; } /* 从节点 i 开始,从顶至底堆化 */ void siftDown(MaxHeap *maxHeap, int i) { while (true) { // 判断节点 i, l, r 中值最大的节点,记为 max int l = left(maxHeap, i); int r = right(maxHeap, i); int max = i; if (l < size(maxHeap) && maxHeap->data[l] > maxHeap->data[max]) { max = l; } if (r < size(maxHeap) && maxHeap->data[r] > maxHeap->data[max]) { max = r; } // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 if (max == i) { break; } // 交换两节点 swap(maxHeap, i, max); // 循环向下堆化 i = max; } } ``` === "Kotlin" ```kotlin title="my_heap.kt" /* 元素出堆 */ fun pop(): Int { // 判空处理 if (isEmpty()) throw IndexOutOfBoundsException() // 交换根节点与最右叶节点(交换首元素与尾元素) swap(0, size() - 1) // 删除节点 val _val = maxHeap.removeAt(size() - 1) // 从顶至底堆化 siftDown(0) // 返回堆顶元素 return _val } /* 从节点 i 开始,从顶至底堆化 */ fun siftDown(it: Int) { // Kotlin的函数参数不可变,因此创建临时变量 var i = it while (true) { // 判断节点 i, l, r 中值最大的节点,记为 ma val l = left(i) val r = right(i) var ma = i if (l < size() && maxHeap[l] > maxHeap[ma]) ma = l if (r < size() && maxHeap[r] > maxHeap[ma]) ma = r // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 if (ma == i) break // 交换两节点 swap(i, ma) // 循环向下堆化 i = ma } } ``` === "Ruby" ```ruby title="my_heap.rb" [class]{MaxHeap}-[func]{pop} [class]{MaxHeap}-[func]{sift_down} ``` === "Zig" ```zig title="my_heap.zig" // 元素出堆 fn pop(self: *Self) !T { // 判断处理 if (self.isEmpty()) unreachable; // 交换根节点与最右叶节点(交换首元素与尾元素) try self.swap(0, self.size() - 1); // 删除节点 var val = self.max_heap.?.pop(); // 从顶至底堆化 try self.siftDown(0); // 返回堆顶元素 return val; } // 从节点 i 开始,从顶至底堆化 fn siftDown(self: *Self, i_: usize) !void { var i = i_; while (true) { // 判断节点 i, l, r 中值最大的节点,记为 ma var l = left(i); var r = right(i); var ma = i; if (l < self.size() and self.max_heap.?.items[l] > self.max_heap.?.items[ma]) ma = l; if (r < self.size() and self.max_heap.?.items[r] > self.max_heap.?.items[ma]) ma = r; // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 if (ma == i) break; // 交换两节点 try self.swap(i, ma); // 循环向下堆化 i = ma; } } ``` ??? pythontutor "Code Visualization" ## 8.1.3 Common applications of heaps - **Priority Queue**: Heaps are often the preferred data structure for implementing priority queues, with both enqueue and dequeue operations having a time complexity of $O(\log n)$, and building a queue having a time complexity of $O(n)$, all of which are very efficient. - **Heap Sort**: Given a set of data, we can create a heap from them and then continually perform element removal operations to obtain ordered data. However, we usually use a more elegant method to implement heap sort, as detailed in the "Heap Sort" section. - **Finding the Largest $k$ Elements**: This is a classic algorithm problem and also a typical application, such as selecting the top 10 hot news for Weibo hot search, picking the top 10 selling products, etc.