ic_stable_structures/
min_heap.rs

1use crate::base_vec::{BaseVec, InitError};
2use crate::storable::Storable;
3use crate::{GrowFailed, Memory};
4use std::fmt;
5
6#[cfg(test)]
7mod tests;
8
9const MAGIC: [u8; 3] = *b"SMH"; // Short for "stable min heap".
10
11/// An implementation of the [binary min heap](https://en.wikipedia.org/wiki/Binary_heap).
12// NB. Contrary to [std::collections::BinaryHeap], this heap is a min-heap (smallest items come first).
13// Motivation: max heaps are helpful for sorting, but most daily programming tasks require min
14// heaps.
15pub struct MinHeap<T: Storable + PartialOrd, M: Memory>(BaseVec<T, M>);
16
17// Note: Heap Invariant
18// ~~~~~~~~~~~~~~~~~~~~
19//
20// HeapInvariant(heap, i, j) :=
21//   ∀ k: i ≤ k ≤ j: LET p = (k - 1)/2 IN (p ≤ i) => heap[p] ≤ heap[k]
22
23impl<T, M> MinHeap<T, M>
24where
25    T: Storable + PartialOrd,
26    M: Memory,
27{
28    /// Creates a new empty heap in the specified memory,
29    /// overwriting any data structures the memory might have
30    /// contained.
31    ///
32    /// Complexity: O(1)
33    pub fn new(memory: M) -> Result<Self, GrowFailed> {
34        BaseVec::<T, M>::new(memory, MAGIC).map(Self)
35    }
36
37    /// Initializes a heap in the specified memory.
38    ///
39    /// Complexity: O(1)
40    ///
41    /// PRECONDITION: the memory is either empty or contains a valid
42    /// stable heap.
43    pub fn init(memory: M) -> Result<Self, InitError> {
44        BaseVec::<T, M>::init(memory, MAGIC).map(Self)
45    }
46
47    /// Returns the number of items in the heap.
48    ///
49    /// Complexity: O(1)
50    pub fn len(&self) -> u64 {
51        self.0.len()
52    }
53
54    /// Returns true if the heap is empty.
55    ///
56    /// Complexity: O(1)
57    pub fn is_empty(&self) -> bool {
58        self.0.is_empty()
59    }
60
61    /// Pushes an item onto the heap.
62    ///
63    /// Complexity: O(log(self.len()))
64    pub fn push(&mut self, item: &T) -> Result<(), GrowFailed> {
65        self.0.push(item)?;
66        self.bubble_up(self.0.len() - 1, item);
67        debug_assert_eq!(Ok(()), self.check_invariant());
68        Ok(())
69    }
70
71    /// Removes the smallest item from the heap and returns it.
72    /// Returns `None` if the heap is empty.
73    ///
74    /// Complexity: O(log(self.len()))
75    pub fn pop(&mut self) -> Option<T> {
76        let n = self.len();
77        match n {
78            0 => None,
79            1 => self.0.pop(),
80            _more => {
81                let smallest = self.0.get(0).unwrap();
82                let last = self.0.pop().unwrap();
83                self.0.set(0, &last);
84                self.bubble_down(0, n - 1, &last);
85                debug_assert_eq!(Ok(()), self.check_invariant());
86                Some(smallest)
87            }
88        }
89    }
90
91    /// Returns the smallest item in the heap.
92    /// Returns `None` if the heap is empty.
93    ///
94    /// Complexity: O(1)
95    pub fn peek(&self) -> Option<T> {
96        self.0.get(0)
97    }
98
99    /// Returns an iterator visiting all values in the underlying vector, in arbitrary order.
100    pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
101        self.0.iter()
102    }
103
104    /// Returns the underlying memory instance.
105    pub fn into_memory(self) -> M {
106        self.0.into_memory()
107    }
108
109    #[allow(dead_code)]
110    /// Checks the HeapInvariant(self, 0, self.len() - 1)
111    fn check_invariant(&self) -> Result<(), String> {
112        let n = self.len();
113        for i in 1..n {
114            let p = (i - 1) / 2;
115            let item = self.0.get(i).unwrap();
116            let parent = self.0.get(p).unwrap();
117            if is_less(&item, &parent) {
118                return Err(format!(
119                    "Binary heap invariant violated in indices {i} and {p}"
120                ));
121            }
122        }
123        Ok(())
124    }
125
126    /// PRECONDITION: self.0.get(i) == item
127    fn bubble_up(&mut self, mut i: u64, item: &T) {
128        // We set the flag if self.0.get(i) does not contain the item anymore.
129        let mut swapped = false;
130        // LOOP INVARIANT: HeapInvariant(self, i, self.len() - 1)
131        while i > 0 {
132            let p = (i - 1) / 2;
133            let parent = self.0.get(p).unwrap();
134            if is_less(item, &parent) {
135                self.0.set(i, &parent);
136                swapped = true;
137            } else {
138                break;
139            }
140            i = p;
141        }
142        if swapped {
143            self.0.set(i, item);
144        }
145    }
146
147    /// PRECONDITION: self.0.get(i) == item
148    fn bubble_down(&mut self, mut i: u64, n: u64, item: &T) {
149        // We set the flag if self.0.get(i) does not contain the item anymore.
150        let mut swapped = false;
151        // LOOP INVARIANT: HeapInvariant(self, 0, i)
152        loop {
153            let l = i * 2 + 1;
154            let r = l + 1;
155
156            if n <= l {
157                break;
158            }
159
160            if n <= r {
161                // Only the left child is within the array bounds.
162
163                let left = self.0.get(l).unwrap();
164                if is_less(&left, item) {
165                    self.0.set(i, &left);
166                    swapped = true;
167                    i = l;
168                    continue;
169                }
170            } else {
171                // Both children are within the array bounds.
172
173                let left = self.0.get(l).unwrap();
174                let right = self.0.get(r).unwrap();
175
176                let (min_index, min_elem) = if is_less(&left, &right) {
177                    (l, &left)
178                } else {
179                    (r, &right)
180                };
181
182                if is_less(min_elem, item) {
183                    self.0.set(i, min_elem);
184                    swapped = true;
185                    i = min_index;
186                    continue;
187                }
188            }
189            break;
190        }
191        if swapped {
192            self.0.set(i, item);
193        }
194    }
195}
196
197fn is_less<T: PartialOrd>(x: &T, y: &T) -> bool {
198    x.partial_cmp(y) == Some(std::cmp::Ordering::Less)
199}
200
201impl<T, M> fmt::Debug for MinHeap<T, M>
202where
203    T: Storable + PartialOrd + fmt::Debug,
204    M: Memory,
205{
206    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
207        self.0.fmt(fmt)
208    }
209}