--- comments: true --- # 8.1 Heap A heap is a complete binary tree that satisfies specific conditions and can be mainly categorized into two types, as shown in 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. { 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. - We call the root node of the binary tree the "heap top" and the bottom-rightmost node 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 Heap Operations It should be noted that many programming languages provide a priority queue, an abstract data structure defined as a queue whose elements are ordered by priority. In fact, **heaps are typically used to implement priority queues, with max heaps corresponding to priority queues where elements are dequeued in descending order**. From a usage perspective, we can regard "priority queue" and "heap" as equivalent data structures. Therefore, this book does not make a special distinction between the two and uniformly refers to them as "heap." Common heap operations are shown in Table 8-1, and method names need to be determined based 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 subsequent use: === "Python" ```python title="my_heap.py" def left(self, i: int) -> int: """Get index of left child node""" return 2 * i + 1 def right(self, i: int) -> int: """Get index of right child node""" return 2 * i + 2 def parent(self, i: int) -> int: """Get index of parent node""" return (i - 1) // 2 # Floor division ``` === "C++" ```cpp title="my_heap.cpp" /* Get index of left child node */ int left(int i) { return 2 * i + 1; } /* Get index of right child node */ int right(int i) { return 2 * i + 2; } /* Get index of parent node */ int parent(int i) { return (i - 1) / 2; // Floor division } ``` === "Java" ```java title="my_heap.java" /* Get index of left child node */ int left(int i) { return 2 * i + 1; } /* Get index of right child node */ int right(int i) { return 2 * i + 2; } /* Get index of parent node */ int parent(int i) { return (i - 1) / 2; // Floor division } ``` === "C#" ```csharp title="my_heap.cs" /* Get index of left child node */ int Left(int i) { return 2 * i + 1; } /* Get index of right child node */ int Right(int i) { return 2 * i + 2; } /* Get index of parent node */ int Parent(int i) { return (i - 1) / 2; // Floor division } ``` === "Go" ```go title="my_heap.go" /* Get index of left child node */ func (h *maxHeap) left(i int) int { return 2*i + 1 } /* Get index of right child node */ func (h *maxHeap) right(i int) int { return 2*i + 2 } /* Get index of parent node */ func (h *maxHeap) parent(i int) int { // Floor division return (i - 1) / 2 } ``` === "Swift" ```swift title="my_heap.swift" /* Get index of left child node */ func left(i: Int) -> Int { 2 * i + 1 } /* Get index of right child node */ func right(i: Int) -> Int { 2 * i + 2 } /* Get index of parent node */ func parent(i: Int) -> Int { (i - 1) / 2 // Floor division } ``` === "JS" ```javascript title="my_heap.js" /* Get index of left child node */ #left(i) { return 2 * i + 1; } /* Get index of right child node */ #right(i) { return 2 * i + 2; } /* Get index of parent node */ #parent(i) { return Math.floor((i - 1) / 2); // Floor division } ``` === "TS" ```typescript title="my_heap.ts" /* Get index of left child node */ left(i: number): number { return 2 * i + 1; } /* Get index of right child node */ right(i: number): number { return 2 * i + 2; } /* Get index of parent node */ parent(i: number): number { return Math.floor((i - 1) / 2); // Floor division } ``` === "Dart" ```dart title="my_heap.dart" /* Get index of left child node */ int _left(int i) { return 2 * i + 1; } /* Get index of right child node */ int _right(int i) { return 2 * i + 2; } /* Get index of parent node */ int _parent(int i) { return (i - 1) ~/ 2; // Floor division } ``` === "Rust" ```rust title="my_heap.rs" /* Get index of left child node */ fn left(i: usize) -> usize { 2 * i + 1 } /* Get index of right child node */ fn right(i: usize) -> usize { 2 * i + 2 } /* Get index of parent node */ fn parent(i: usize) -> usize { (i - 1) / 2 // Floor division } ``` === "C" ```c title="my_heap.c" /* Get index of left child node */ int left(MaxHeap *maxHeap, int i) { return 2 * i + 1; } /* Get index of right child node */ int right(MaxHeap *maxHeap, int i) { return 2 * i + 2; } /* Get index of parent node */ int parent(MaxHeap *maxHeap, int i) { return (i - 1) / 2; // Round down } ``` === "Kotlin" ```kotlin title="my_heap.kt" /* Get index of left child node */ fun left(i: Int): Int { return 2 * i + 1 } /* Get index of right child node */ fun right(i: Int): Int { return 2 * i + 2 } /* Get index of parent node */ fun parent(i: Int): Int { return (i - 1) / 2 // Floor division } ``` === "Ruby" ```ruby title="my_heap.rb" ### Get left child index ### def left(i) 2 * i + 1 end ### Get right child index ### def right(i) 2 * i + 2 end ### Get parent node index ### def parent(i) (i - 1) / 2 # Floor division end ``` ### 2. Accessing the Heap Top Element The heap top element 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: """Access top element""" return self.max_heap[0] ``` === "C++" ```cpp title="my_heap.cpp" /* Access top element */ int peek() { return maxHeap[0]; } ``` === "Java" ```java title="my_heap.java" /* Access top element */ int peek() { return maxHeap.get(0); } ``` === "C#" ```csharp title="my_heap.cs" /* Access top element */ int Peek() { return maxHeap[0]; } ``` === "Go" ```go title="my_heap.go" /* Access top element */ func (h *maxHeap) peek() any { return h.data[0] } ``` === "Swift" ```swift title="my_heap.swift" /* Access top element */ func peek() -> Int { maxHeap[0] } ``` === "JS" ```javascript title="my_heap.js" /* Access top element */ peek() { return this.#maxHeap[0]; } ``` === "TS" ```typescript title="my_heap.ts" /* Access top element */ peek(): number { return this.maxHeap[0]; } ``` === "Dart" ```dart title="my_heap.dart" /* Access top element */ int peek() { return _maxHeap[0]; } ``` === "Rust" ```rust title="my_heap.rs" /* Access top element */ fn peek(&self) -> OptionFigure 8-3 Steps of inserting an element into the heap
Given a total of $n$ nodes, the tree height is $O(\log n)$. Thus, the number of loop iterations in the heapify operation is at most $O(\log n)$, **making the time complexity of the element insertion operation $O(\log n)$**. The code is as follows: === "Python" ```python title="my_heap.py" def push(self, val: int): """Element enters heap""" # Add node self.max_heap.append(val) # Heapify from bottom to top self.sift_up(self.size() - 1) def sift_up(self, i: int): """Starting from node i, heapify from bottom to top""" while True: # Get parent node of node i p = self.parent(i) # When "crossing root node" or "node needs no repair", end heapify if p < 0 or self.max_heap[i] <= self.max_heap[p]: break # Swap two nodes self.swap(i, p) # Loop upward heapify i = p ``` === "C++" ```cpp title="my_heap.cpp" /* Element enters heap */ void push(int val) { // Add node maxHeap.push_back(val); // Heapify from bottom to top siftUp(size() - 1); } /* Starting from node i, heapify from bottom to top */ void siftUp(int i) { while (true) { // Get parent node of node i int p = parent(i); // When "crossing root node" or "node needs no repair", end heapify if (p < 0 || maxHeap[i] <= maxHeap[p]) break; // Swap two nodes swap(maxHeap[i], maxHeap[p]); // Loop upward heapify i = p; } } ``` === "Java" ```java title="my_heap.java" /* Element enters heap */ void push(int val) { // Add node maxHeap.add(val); // Heapify from bottom to top siftUp(size() - 1); } /* Starting from node i, heapify from bottom to top */ void siftUp(int i) { while (true) { // Get parent node of node i int p = parent(i); // When "crossing root node" or "node needs no repair", end heapify if (p < 0 || maxHeap.get(i) <= maxHeap.get(p)) break; // Swap two nodes swap(i, p); // Loop upward heapify i = p; } } ``` === "C#" ```csharp title="my_heap.cs" /* Element enters heap */ void Push(int val) { // Add node maxHeap.Add(val); // Heapify from bottom to top SiftUp(Size() - 1); } /* Starting from node i, heapify from bottom to top */ void SiftUp(int i) { while (true) { // Get parent node of node i int p = Parent(i); // If 'past root node' or 'node needs no repair', end heapify if (p < 0 || maxHeap[i] <= maxHeap[p]) break; // Swap two nodes Swap(i, p); // Loop upward heapify i = p; } } ``` === "Go" ```go title="my_heap.go" /* Element enters heap */ func (h *maxHeap) push(val any) { // Add node h.data = append(h.data, val) // Heapify from bottom to top h.siftUp(len(h.data) - 1) } /* Starting from node i, heapify from bottom to top */ func (h *maxHeap) siftUp(i int) { for true { // Get parent node of node i p := h.parent(i) // When "crossing root node" or "node needs no repair", end heapify if p < 0 || h.data[i].(int) <= h.data[p].(int) { break } // Swap two nodes h.swap(i, p) // Loop upward heapify i = p } } ``` === "Swift" ```swift title="my_heap.swift" /* Element enters heap */ func push(val: Int) { // Add node maxHeap.append(val) // Heapify from bottom to top siftUp(i: size() - 1) } /* Starting from node i, heapify from bottom to top */ func siftUp(i: Int) { var i = i while true { // Get parent node of node i let p = parent(i: i) // When "crossing root node" or "node needs no repair", end heapify if p < 0 || maxHeap[i] <= maxHeap[p] { break } // Swap two nodes swap(i: i, j: p) // Loop upward heapify i = p } } ``` === "JS" ```javascript title="my_heap.js" /* Element enters heap */ push(val) { // Add node this.#maxHeap.push(val); // Heapify from bottom to top this.#siftUp(this.size() - 1); } /* Starting from node i, heapify from bottom to top */ #siftUp(i) { while (true) { // Get parent node of node i const p = this.#parent(i); // When "crossing root node" or "node needs no repair", end heapify if (p < 0 || this.#maxHeap[i] <= this.#maxHeap[p]) break; // Swap two nodes this.#swap(i, p); // Loop upward heapify i = p; } } ``` === "TS" ```typescript title="my_heap.ts" /* Element enters heap */ push(val: number): void { // Add node this.maxHeap.push(val); // Heapify from bottom to top this.siftUp(this.size() - 1); } /* Starting from node i, heapify from bottom to top */ siftUp(i: number): void { while (true) { // Get parent node of node i const p = this.parent(i); // When "crossing root node" or "node needs no repair", end heapify if (p < 0 || this.maxHeap[i] <= this.maxHeap[p]) break; // Swap two nodes this.swap(i, p); // Loop upward heapify i = p; } } ``` === "Dart" ```dart title="my_heap.dart" /* Element enters heap */ void push(int val) { // Add node _maxHeap.add(val); // Heapify from bottom to top siftUp(size() - 1); } /* Starting from node i, heapify from bottom to top */ void siftUp(int i) { while (true) { // Get parent node of node i int p = _parent(i); // When "crossing root node" or "node needs no repair", end heapify if (p < 0 || _maxHeap[i] <= _maxHeap[p]) { break; } // Swap two nodes _swap(i, p); // Loop upward heapify i = p; } } ``` === "Rust" ```rust title="my_heap.rs" /* Element enters heap */ fn push(&mut self, val: i32) { // Add node self.max_heap.push(val); // Heapify from bottom to top self.sift_up(self.size() - 1); } /* Starting from node i, heapify from bottom to top */ fn sift_up(&mut self, mut i: usize) { loop { // Node i is already the heap root, end heapification if i == 0 { break; } // Get parent node of node i let p = Self::parent(i); // When "node needs no repair", end heapification if self.max_heap[i] <= self.max_heap[p] { break; } // Swap two nodes self.swap(i, p); // Loop upward heapify i = p; } } ``` === "C" ```c title="my_heap.c" /* Element enters heap */ void push(MaxHeap *maxHeap, int val) { // By default, should not add this many nodes if (maxHeap->size == MAX_SIZE) { printf("heap is full!"); return; } // Add node maxHeap->data[maxHeap->size] = val; maxHeap->size++; // Heapify from bottom to top siftUp(maxHeap, maxHeap->size - 1); } /* Starting from node i, heapify from bottom to top */ void siftUp(MaxHeap *maxHeap, int i) { while (true) { // Get parent node of node i int p = parent(maxHeap, i); // When "crossing root node" or "node needs no repair", end heapify if (p < 0 || maxHeap->data[i] <= maxHeap->data[p]) { break; } // Swap two nodes swap(maxHeap, i, p); // Loop upward heapify i = p; } } ``` === "Kotlin" ```kotlin title="my_heap.kt" /* Element enters heap */ fun push(_val: Int) { // Add node maxHeap.add(_val) // Heapify from bottom to top siftUp(size() - 1) } /* Starting from node i, heapify from bottom to top */ fun siftUp(it: Int) { // Kotlin function parameters are immutable, so create temporary variable var i = it while (true) { // Get parent node of node i val p = parent(i) // When "crossing root node" or "node needs no repair", end heapify if (p < 0 || maxHeap[i] <= maxHeap[p]) break // Swap two nodes swap(i, p) // Loop upward heapify i = p } } ``` === "Ruby" ```ruby title="my_heap.rb" ### Push element to heap ### def push(val) # Add node @max_heap << val # Heapify from bottom to top sift_up(size - 1) end ### Heapify from node i, bottom to top ### def sift_up(i) loop do # Get parent node of node i p = parent(i) # When "crossing root node" or "node needs no repair", end heapify break if p < 0 || @max_heap[i] <= @max_heap[p] # Swap two nodes swap(i, p) # Loop upward heapify i = p end end ``` ### 4. Removing the Heap Top Element The heap top element is the root node of the binary tree, which 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 subsequent repair with heapify difficult. To minimize changes in element indexes, we use the following steps. 1. Swap the heap top element with the heap bottom element (swap the root node with the rightmost leaf node). 2. After swapping, remove the heap bottom from the list (note that since we've swapped, we're actually removing the original heap top element). 3. Starting from the root node, **perform heapify from top to bottom**. As shown in Figure 8-4, **the direction of "top-to-bottom heapify" is opposite to "bottom-to-top heapify"**. We compare the root node's value with its two children and swap it with the largest child. Then loop this operation until we pass a leaf node or encounter a node that doesn't need swapping. === "<1>" { class="animation-figure" } === "<2>" { class="animation-figure" } === "<3>" { class="animation-figure" } === "<4>" { class="animation-figure" } === "<5>" { class="animation-figure" } === "<6>" { class="animation-figure" } === "<7>" { class="animation-figure" } === "<8>" { class="animation-figure" } === "<9>" { class="animation-figure" } === "<10>" { class="animation-figure" }Figure 8-4 Steps of removing the heap top element
Similar to the element insertion operation, the time complexity of the heap top element removal operation is also $O(\log n)$. The code is as follows: === "Python" ```python title="my_heap.py" def pop(self) -> int: """Element exits heap""" # Handle empty case if self.is_empty(): raise IndexError("Heap is empty") # Swap root node with rightmost leaf node (swap first element with last element) self.swap(0, self.size() - 1) # Delete node val = self.max_heap.pop() # Heapify from top to bottom self.sift_down(0) # Return top element return val def sift_down(self, i: int): """Starting from node i, heapify from top to bottom""" while True: # Find node with largest value among i, l, r, denoted as 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 # If node i is largest or indices l, r are out of bounds, no need to continue heapify, break if ma == i: break # Swap two nodes self.swap(i, ma) # Loop downward heapify i = ma ``` === "C++" ```cpp title="my_heap.cpp" /* Element exits heap */ void pop() { // Handle empty case if (isEmpty()) { throw out_of_range("Heap is empty"); } // Delete node swap(maxHeap[0], maxHeap[size() - 1]); // Remove node maxHeap.pop_back(); // Return top element siftDown(0); } /* Starting from node i, heapify from top to bottom */ void siftDown(int i) { while (true) { // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break 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; // Swap two nodes if (ma == i) break; swap(maxHeap[i], maxHeap[ma]); // Loop downwards heapification i = ma; } } ``` === "Java" ```java title="my_heap.java" /* Element exits heap */ int pop() { // Handle empty case if (isEmpty()) throw new IndexOutOfBoundsException(); // Delete node swap(0, size() - 1); // Remove node int val = maxHeap.remove(size() - 1); // Return top element siftDown(0); // Return heap top element return val; } /* Starting from node i, heapify from top to bottom */ void siftDown(int i) { while (true) { // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break 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; // Swap two nodes if (ma == i) break; // Swap two nodes swap(i, ma); // Loop downwards heapification i = ma; } } ``` === "C#" ```csharp title="my_heap.cs" /* Element exits heap */ int Pop() { // Handle empty case if (IsEmpty()) throw new IndexOutOfRangeException(); // Delete node Swap(0, Size() - 1); // Remove node int val = maxHeap.Last(); maxHeap.RemoveAt(Size() - 1); // Return top element SiftDown(0); // Return heap top element return val; } /* Starting from node i, heapify from top to bottom */ void SiftDown(int i) { while (true) { // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break 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; // If 'node i is largest' or 'past leaf node', end heapify if (ma == i) break; // Swap two nodes Swap(i, ma); // Loop downwards heapification i = ma; } } ``` === "Go" ```go title="my_heap.go" /* Element exits heap */ func (h *maxHeap) pop() any { // Handle empty case if h.isEmpty() { fmt.Println("error") return nil } // Delete node h.swap(0, h.size()-1) // Remove node val := h.data[len(h.data)-1] h.data = h.data[:len(h.data)-1] // Return top element h.siftDown(0) // Return heap top element return val } /* Starting from node i, heapify from top to bottom */ func (h *maxHeap) siftDown(i int) { for true { // Find node with maximum value among nodes i, l, r, denoted as 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 } // Swap two nodes if max == i { break } // Swap two nodes h.swap(i, max) // Loop downwards heapification i = max } } ``` === "Swift" ```swift title="my_heap.swift" /* Element exits heap */ func pop() -> Int { // Handle empty case if isEmpty() { fatalError("Heap is empty") } // Delete node swap(i: 0, j: size() - 1) // Remove node let val = maxHeap.remove(at: size() - 1) // Return top element siftDown(i: 0) // Return heap top element return val } /* Starting from node i, heapify from top to bottom */ func siftDown(i: Int) { var i = i while true { // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break 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 } // Swap two nodes if ma == i { break } // Swap two nodes swap(i: i, j: ma) // Loop downwards heapification i = ma } } ``` === "JS" ```javascript title="my_heap.js" /* Element exits heap */ pop() { // Handle empty case if (this.isEmpty()) throw new Error('Heap is empty'); // Delete node this.#swap(0, this.size() - 1); // Remove node const val = this.#maxHeap.pop(); // Return top element this.#siftDown(0); // Return heap top element return val; } /* Starting from node i, heapify from top to bottom */ #siftDown(i) { while (true) { // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break 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; // Swap two nodes if (ma === i) break; // Swap two nodes this.#swap(i, ma); // Loop downwards heapification i = ma; } } ``` === "TS" ```typescript title="my_heap.ts" /* Element exits heap */ pop(): number { // Handle empty case if (this.isEmpty()) throw new RangeError('Heap is empty.'); // Delete node this.swap(0, this.size() - 1); // Remove node const val = this.maxHeap.pop(); // Return top element this.siftDown(0); // Return heap top element return val; } /* Starting from node i, heapify from top to bottom */ siftDown(i: number): void { while (true) { // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break 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; // Swap two nodes if (ma === i) break; // Swap two nodes this.swap(i, ma); // Loop downwards heapification i = ma; } } ``` === "Dart" ```dart title="my_heap.dart" /* Element exits heap */ int pop() { // Handle empty case if (isEmpty()) throw Exception('Heap is empty'); // Delete node _swap(0, size() - 1); // Remove node int val = _maxHeap.removeLast(); // Return top element siftDown(0); // Return heap top element return val; } /* Starting from node i, heapify from top to bottom */ void siftDown(int i) { while (true) { // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break 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; // Swap two nodes if (ma == i) break; // Swap two nodes _swap(i, ma); // Loop downwards heapification i = ma; } } ``` === "Rust" ```rust title="my_heap.rs" /* Element exits heap */ fn pop(&mut self) -> i32 { // Handle empty case if self.is_empty() { panic!("index out of bounds"); } // Delete node self.swap(0, self.size() - 1); // Remove node let val = self.max_heap.pop().unwrap(); // Return top element self.sift_down(0); // Return heap top element val } /* Starting from node i, heapify from top to bottom */ fn sift_down(&mut self, mut i: usize) { loop { // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break 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; } // Swap two nodes if ma == i { break; } // Swap two nodes self.swap(i, ma); // Loop downwards heapification i = ma; } } ``` === "C" ```c title="my_heap.c" /* Element exits heap */ int pop(MaxHeap *maxHeap) { // Handle empty case if (isEmpty(maxHeap)) { printf("heap is empty!"); return INT_MAX; } // Delete node swap(maxHeap, 0, size(maxHeap) - 1); // Remove node int val = maxHeap->data[maxHeap->size - 1]; maxHeap->size--; // Return top element siftDown(maxHeap, 0); // Return heap top element return val; } /* Starting from node i, heapify from top to bottom */ void siftDown(MaxHeap *maxHeap, int i) { while (true) { // Find node with maximum value among nodes i, l, r, denoted as 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; } // Swap two nodes if (max == i) { break; } // Swap two nodes swap(maxHeap, i, max); // Loop downwards heapification i = max; } } ``` === "Kotlin" ```kotlin title="my_heap.kt" /* Element exits heap */ fun pop(): Int { // Handle empty case if (isEmpty()) throw IndexOutOfBoundsException() // Delete node swap(0, size() - 1) // Remove node val _val = maxHeap.removeAt(size() - 1) // Return top element siftDown(0) // Return heap top element return _val } /* Starting from node i, heapify from top to bottom */ fun siftDown(it: Int) { // Kotlin function parameters are immutable, so create temporary variable var i = it while (true) { // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break 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 // Swap two nodes if (ma == i) break // Swap two nodes swap(i, ma) // Loop downwards heapification i = ma } } ``` === "Ruby" ```ruby title="my_heap.rb" ### Pop element from heap ### def pop # Handle empty case raise IndexError, "Heap is empty" if is_empty? # Delete node swap(0, size - 1) # Remove node val = @max_heap.pop # Return top element sift_down(0) # Return heap top element val end ### Heapify from node i, top to bottom ### def sift_down(i) loop do # If node i is largest or indices l, r are out of bounds, no need to continue heapify, break l, r, ma = left(i), right(i), i ma = l if l < size && @max_heap[l] > @max_heap[ma] ma = r if r < size && @max_heap[r] > @max_heap[ma] # Swap two nodes break if ma == i # Swap two nodes swap(i, ma) # Loop downwards heapification i = ma end end ``` ## 8.1.3 Common Applications of Heaps - **Priority queue**: Heaps are typically the preferred data structure for implementing priority queues. The time complexity of both enqueue and dequeue operations is $O(\log n)$, and heap construction has a time complexity of $O(n)$, making these operations highly efficient. - **Heap sort**: Given a set of data, we can build a heap with them and then continuously perform element removal operations to obtain sorted data. However, we usually use a more elegant approach to implement heap sort, as detailed in the "Heap Sort" chapter. - **Getting the largest $k$ elements**: This is a classic algorithm problem and also a typical application, such as selecting the top 10 trending news items for Weibo Hot Search or the top 10 best-selling products.