tree_experiments/
binary_heap.rs

1// SPDX-FileCopyrightText: 2023 Markus Mayer
2// SPDX-License-Identifier: EUPL-1.2
3
4use std::fmt::{Debug, Formatter};
5use std::marker::PhantomData;
6use std::mem::swap;
7
8/// A binary max-heap.
9///
10/// Uses a list representation internally.
11pub type BinaryMaxHeap<T> = BinaryHeap<T, Max<T>>;
12
13/// A binary max-heap.
14///
15/// Uses a list representation internally.
16pub type BinaryMinHeap<T> = BinaryHeap<T, Min<T>>;
17
18/// A binary tree with an arbitrary heap property.
19pub struct BinaryHeap<T, H> {
20    /// The heap in list representation.
21    ///
22    /// ## List representation
23    ///
24    /// An element at index `N` has its left child at index `2×N+1` and its right child at `2×N+2`.
25    /// The parent of a non-root element at `N>0` can be found at index `(N-1)/2`.
26    ///
27    /// ## Complete Binary Tree
28    ///
29    /// The heap is implemented as a complete binary tree, i.e. all levels are completely filled,
30    /// except for possibly the last level, i.e. all children are as far left as possible, and a
31    /// node has either both a left and right child, just a left child or no children at all.
32    data: Vec<T>,
33    _heap_property: PhantomData<H>,
34}
35
36/// Implements the max-heap property.
37pub struct Max<T>(PhantomData<T>);
38
39/// Implements the min-heap property.
40pub struct Min<T>(PhantomData<T>);
41
42/// Heap property.
43pub trait HeapProperty<T> {
44    const NAME: &'static str;
45
46    /// Determines whether the heap property is upheld between the first and the
47    /// second value, where the first value takes priority:
48    ///
49    /// * `property_upheld(parent, child)`
50    /// * `property_upheld(left, right)`
51    fn property_upheld(lhs: &T, rhs: &T) -> bool;
52}
53
54impl<T, H> BinaryHeap<T, H> {
55    /// Constructs a new, empty `BinaryHeap`.
56    pub const fn new() -> Self {
57        Self {
58            data: Vec::new(),
59            _heap_property: PhantomData,
60        }
61    }
62
63    /// Constructs a new, empty `BinaryHeap` with at least the specified capacity.
64    pub fn with_capacity(capacity: usize) -> Self {
65        Self {
66            data: Vec::with_capacity(capacity),
67            _heap_property: PhantomData,
68        }
69    }
70
71    /// Pushes an element onto the heap.
72    ///
73    /// ## Arguments
74    ///
75    /// * `value` - The value to push onto the heap.
76    pub fn push(&mut self, value: T)
77    where
78        T: PartialOrd,
79        H: HeapProperty<T>,
80    {
81        let index = self.data.len();
82        self.data.push(value);
83        self.upheap(index);
84    }
85
86    /// Gets a reference to the top element on the heap.
87    ///
88    /// ## Returns
89    ///
90    /// A value if the tree is nonempty; or `None` otherwise.
91    pub fn peek(&self) -> Option<&T> {
92        self.data.get(0)
93    }
94
95    /// Extracts an element from the heap.
96    ///
97    /// ## Returns
98    ///
99    /// A value if the tree is nonempty; or `None` otherwise.
100    pub fn pop(&mut self) -> Option<T>
101    where
102        T: PartialOrd,
103        H: HeapProperty<T>,
104    {
105        if self.data.is_empty() {
106            return None;
107        }
108
109        // Replace the root of the heap with the last element on the last level.
110        let last_index = self.data.len() - 1;
111        self.data.swap(0, last_index);
112
113        // We extract the last element first so it doesn't interfere with the downheap operation.
114        let extracted_value = self.data.pop();
115
116        if !self.data.is_empty() {
117            self.downheap(0);
118        }
119
120        extracted_value
121    }
122
123    /// Pushes an element onto the heap and extracts the top value in one operation.
124    ///
125    /// ## Arguments
126    ///
127    /// * `value` - The value to push onto the heap.
128    ///
129    /// ## Returns
130    ///
131    /// The value. If the tree is empty, the value itself is returned.
132    pub fn push_pop(&mut self, mut value: T) -> T
133    where
134        T: PartialOrd,
135        H: HeapProperty<T>,
136    {
137        if self.data.is_empty() {
138            return value;
139        }
140
141        if H::property_upheld(&value, &self.data[0]) {
142            return value;
143        }
144
145        swap(&mut self.data[0], &mut value);
146        self.downheap(0);
147
148        value
149    }
150
151    /// Determines whether the heap contains the specified item.
152    ///
153    /// ## Arguments
154    ///
155    /// * `value` - The value to test for.
156    ///
157    /// ## Returns
158    ///
159    /// Returns `true` if the heap contains the specified item or `false` if not.
160    pub fn contains(&self, value: &T) -> bool
161    where
162        T: PartialEq,
163    {
164        self.data.contains(value)
165    }
166
167    /// Deletes a value from the heap.
168    ///
169    /// ## Arguments
170    ///
171    /// * `value` - The value to delete.
172    ///
173    /// ## Returns
174    ///
175    /// Returns `true` if the value was deleted or `false` if not.
176    pub fn remove(&mut self, value: &T) -> bool
177    where
178        T: PartialEq + PartialOrd,
179        H: HeapProperty<T>,
180    {
181        self.remove_where(|x| x == value).is_some()
182    }
183
184    /// Deletes a value from the heap.
185    ///
186    /// ## Arguments
187    ///
188    /// * `predicate` - The predicate used to test for the value. If the predicate matches,
189    ///                 the value is removed and the function returns.
190    ///
191    /// ## Returns
192    ///
193    /// Returns `Some(value)` if the value was deleted or `None` if not.
194    pub fn remove_where<F>(&mut self, predicate: F) -> Option<T>
195    where
196        T: PartialEq + PartialOrd,
197        H: HeapProperty<T>,
198        F: FnMut(&T) -> bool,
199    {
200        if self.data.is_empty() {
201            return None;
202        }
203
204        let last_index = self.data.len() - 1;
205
206        // Find the index of the element we want to delete.
207        if let Some(index) = self.data.iter().position(predicate) {
208            // Swap this element with the last element.
209            self.data.swap(index, last_index);
210
211            // Remove the element.
212            let previous_value = self.data.pop().expect("the value exists");
213
214            // If we deleted the last element, exit.
215            if index == last_index {
216                return Some(previous_value);
217            }
218
219            // Down-heapify or up-heapify to restore the heap property.
220            let up_heapify = H::property_upheld(&self.data[index], &previous_value);
221            if up_heapify {
222                self.upheap(index);
223            } else {
224                self.downheap(index);
225            }
226
227            Some(previous_value)
228        } else {
229            None
230        }
231    }
232
233    /// Replaces a value on the heap.
234    ///
235    /// This function can be used if the replacement value already exists. If the element needs
236    /// to be mutated in place, use [`BinaryHeap::replace_where_with`] instead.
237    ///
238    /// ## Arguments
239    ///
240    /// * `value` - The value to replace.
241    /// * `replacement` - The replacement value.
242    ///
243    /// ## Returns
244    ///
245    /// Returns `Some(previous value)` if the value was replaced or `None` if not.
246    pub fn replace(&mut self, value: &T, replacement: T) -> Option<T>
247    where
248        T: PartialEq + PartialOrd,
249        H: HeapProperty<T>,
250    {
251        self.replace_where(|x| x == value, replacement)
252    }
253
254    /// Replaces a value on the heap.
255    ///
256    /// This function can be used if the replacement value already exists. If the element needs
257    /// to be mutated in place, use [`BinaryHeap::replace_where_with`] instead.
258    ///
259    /// ## Arguments
260    ///
261    /// * `predicate` - The predicate used to test for the value. If the predicate matches,
262    ///                 the value is removed and the function returns.
263    /// * `replacement` - The replacement value.
264    ///
265    /// ## Returns
266    ///
267    /// Returns `Some(previous value)` if the value was replaced or `None` if not.
268    pub fn replace_where<F>(&mut self, predicate: F, mut replacement: T) -> Option<T>
269    where
270        T: PartialEq + PartialOrd,
271        H: HeapProperty<T>,
272        F: FnMut(&T) -> bool,
273    {
274        if self.data.is_empty() {
275            return None;
276        }
277
278        // Find the index of the element we want to delete.
279        if let Some(index) = self.data.iter().position(predicate) {
280            let downheap = H::property_upheld(&self.data[index], &replacement);
281
282            swap(&mut self.data[index], &mut replacement);
283
284            if downheap {
285                self.downheap(index);
286            } else {
287                self.upheap(index);
288            }
289
290            Some(replacement)
291        } else {
292            None
293        }
294    }
295
296    /// Replaces a value on the heap.
297    ///
298    /// This function allows to mutate the value in-place but at the cost of a bit of performance.
299    /// If the replacement value exists beforehand, use [`BinaryHeap::replace_where`] instead.
300    ///
301    /// ## Arguments
302    ///
303    /// * `value` - The value to replace.
304    /// * `replacement` - The function creating the replacement value.
305    ///
306    /// ## Returns
307    ///
308    /// Returns `true` if the value was replaced or `false` if not.
309    pub fn replace_with<R>(&mut self, value: &T, replacement: R) -> bool
310    where
311        T: PartialEq + PartialOrd,
312        H: HeapProperty<T>,
313        R: FnMut(&mut T),
314    {
315        self.replace_where_with(|x| x == value, replacement)
316    }
317
318    /// Replaces a value on the heap.
319    ///
320    /// This function allows to mutate the value in-place but at the cost of a bit of performance.
321    /// If the replacement value exists beforehand, use [`BinaryHeap::replace_where`] instead.
322    ///
323    /// ## Arguments
324    ///
325    /// * `predicate` - The predicate used to test for the value. If the predicate matches,
326    ///                 the value is removed and the function returns.
327    /// * `replacement` - The function creating the replacement value.
328    ///
329    /// ## Returns
330    ///
331    /// Returns `true` if the value was replaced or `false` if not.
332    pub fn replace_where_with<F, R>(&mut self, predicate: F, mut replacement: R) -> bool
333    where
334        T: PartialEq + PartialOrd,
335        H: HeapProperty<T>,
336        F: FnMut(&T) -> bool,
337        R: FnMut(&mut T),
338    {
339        if self.data.is_empty() {
340            return false;
341        }
342
343        // Find the index of the element we want to delete.
344        if let Some(index) = self.data.iter().position(predicate) {
345            // Mutate the element.
346            replacement(&mut self.data[index]);
347
348            // TODO: Optimize these calls? We don't know if the value increased or decreased here.
349            let _ = self.upheap(index) || self.downheap(index);
350
351            true
352        } else {
353            false
354        }
355    }
356
357    /// Returns the number of elements on the heap.
358    pub fn len(&self) -> usize {
359        self.data.len()
360    }
361
362    /// Returns `true` if the heap is empty.
363    pub fn is_empty(&self) -> bool {
364        self.data.is_empty()
365    }
366
367    /// Ensures that elements are in the correct order, starting from a given child node,
368    /// working its way up the heap.
369    ///
370    /// ## Returns
371    ///
372    /// Returns `true` if the tree was changed or `false` if no change was performed.
373    fn upheap(&mut self, mut index: usize) -> bool
374    where
375        T: PartialOrd,
376        H: HeapProperty<T>,
377    {
378        debug_assert!(index < self.data.len());
379        let mut changed = false;
380
381        // Compare the added element with its parent; if they are in the correct order, stop.
382        while index > 0 {
383            let parent_idx = self
384                .get_parent_idx(index)
385                .expect("since the tree was not empty before the insert, the parent must exist");
386
387            let parent = self
388                .data
389                .get(parent_idx)
390                .expect("since the tree was not empty before the insert, the parent must exist");
391
392            if H::property_upheld(&parent, &self.data[index]) {
393                break;
394            }
395
396            // If not, swap the element with its parent and return to the previous step.
397            self.data.swap(parent_idx, index);
398            index = parent_idx;
399
400            changed = true;
401        }
402
403        changed
404    }
405
406    /// Ensures that elements are in the correct order, starting from a given parent node,
407    /// working its way down the heap.
408    ///
409    /// ## Returns
410    ///
411    /// Returns `true` if the tree was changed or `false` if no change was performed.
412    fn downheap(&mut self, mut index: usize) -> bool
413    where
414        T: PartialOrd,
415        H: HeapProperty<T>,
416    {
417        debug_assert!(index < self.data.len());
418        let mut changed = false;
419
420        // Compare the new root with its children; if they are in the correct order, stop.
421        // If not, swap the element with one of its children and return to the previous step.
422        // (Swap with its smaller child in a min-heap and its larger child in a max-heap.)
423        loop {
424            let left_child_idx = self.get_left_child_idx(index);
425            let right_child_idx = self.get_right_child_idx(index);
426
427            let current = &self.data[index];
428
429            match (left_child_idx, right_child_idx) {
430                (Some(left), Some(right)) => {
431                    let left_child = &self.data[left];
432                    let right_child = &self.data[right];
433
434                    if H::property_upheld(&current, left_child)
435                        && H::property_upheld(current, right_child)
436                    {
437                        break;
438                    }
439
440                    if H::property_upheld(left_child, right_child) {
441                        self.data.swap(index, left);
442                        index = left;
443                    } else {
444                        self.data.swap(index, right);
445                        index = right;
446                    }
447
448                    changed = true;
449                }
450                (Some(child), None) => {
451                    let left_child = &self.data[child];
452
453                    if H::property_upheld(current, left_child) {
454                        break;
455                    }
456
457                    self.data.swap(index, child);
458
459                    // Since there is no right child and the tree is balanced, the left child can also not have any children.
460                    debug_assert!(self.get_left_child_idx(child).is_none());
461
462                    changed = true;
463                }
464                (None, Some(_)) => unreachable!("the tree cannot have only a right child"),
465                (None, None) => break,
466            }
467        }
468
469        changed
470    }
471
472    /// Gets the index of the left child of the item at the specified `index`.
473    const fn get_left_child_idx_unchecked(index: usize) -> usize {
474        2 * index + 1
475    }
476
477    /// Gets the index of the left child of the item at the specified `index`.
478    ///
479    /// ## Returns
480    ///
481    /// * `Some(index)` if the element exists in the tree.
482    /// * `None` if the child does not exist.
483    fn get_left_child_idx(&self, index: usize) -> Option<usize> {
484        let idx = Self::get_left_child_idx_unchecked(index);
485        if idx < self.data.len() {
486            Some(idx)
487        } else {
488            None
489        }
490    }
491
492    /// Gets the index of the right child of the item at the specified `index`.
493    const fn get_right_child_idx_unchecked(index: usize) -> usize {
494        2 * index + 2
495    }
496
497    /// Gets the index of the right child of the item at the specified `index`.
498    ///
499    /// ## Returns
500    ///
501    /// * `Some(index)` if the element exists in the tree.
502    /// * `None` if the child does not exist.
503    fn get_right_child_idx(&self, index: usize) -> Option<usize> {
504        let idx = Self::get_right_child_idx_unchecked(index);
505        if idx < self.data.len() {
506            Some(idx)
507        } else {
508            None
509        }
510    }
511
512    /// Gets the index of the parent of the item at the specified `index`.
513    ///
514    /// ## Returns
515    ///
516    /// * `Some(index)` if the element exists in the tree.
517    /// * `None` if the parent does not exist.
518    fn get_parent_idx(&self, index: usize) -> Option<usize> {
519        if index == 0 || index >= self.data.len() {
520            return None;
521        }
522
523        Some((index - 1) / 2)
524    }
525}
526
527impl<T, H> FromIterator<T> for BinaryHeap<T, H>
528where
529    T: PartialOrd,
530    H: HeapProperty<T>,
531{
532    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
533        <Self as From<Vec<T>>>::from(iter.into_iter().collect())
534    }
535}
536
537impl<T, H> From<Vec<T>> for BinaryHeap<T, H>
538where
539    T: PartialOrd,
540    H: HeapProperty<T>,
541{
542    fn from(value: Vec<T>) -> Self {
543        let mut tree = Self {
544            data: value,
545            _heap_property: PhantomData,
546        };
547
548        // Floyd's method.
549        let length = tree.data.len();
550        for i in (0..=(length / 2)).rev() {
551            tree.downheap(i);
552        }
553
554        tree
555    }
556}
557
558impl<T, H> Debug for BinaryHeap<T, H>
559where
560    T: Debug,
561    H: HeapProperty<T>,
562{
563    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
564        f.debug_struct(&format!("Binary{}Heap", H::NAME))
565            .field("data", &self.data)
566            .finish()
567    }
568}
569
570impl<T, H> Default for BinaryHeap<T, H> {
571    fn default() -> Self {
572        Self::new()
573    }
574}
575
576impl<T, H> Clone for BinaryHeap<T, H>
577where
578    T: Clone,
579{
580    fn clone(&self) -> Self {
581        Self {
582            data: self.data.clone(),
583            _heap_property: PhantomData,
584        }
585    }
586}
587
588impl<T, H> AsRef<[T]> for BinaryHeap<T, H> {
589    fn as_ref(&self) -> &[T] {
590        &self.data
591    }
592}
593
594impl<T> HeapProperty<T> for Max<T>
595where
596    T: PartialOrd,
597{
598    const NAME: &'static str = "Max";
599
600    fn property_upheld(lhs: &T, rhs: &T) -> bool {
601        lhs >= rhs
602    }
603}
604
605impl<T> HeapProperty<T> for Min<T>
606where
607    T: PartialOrd,
608{
609    const NAME: &'static str = "Min";
610
611    fn property_upheld(lhs: &T, rhs: &T) -> bool {
612        lhs <= rhs
613    }
614}