1
1
use alloc:: vec:: IntoIter ;
2
2
use core:: cmp:: { Ord , Ordering , Reverse } ;
3
3
use core:: mem:: { replace, transmute, MaybeUninit } ;
4
-
4
+ use core :: ops :: Range ;
5
5
6
6
fn k_smallest_dynamic < T , I : Iterator < Item = T > > (
7
7
iter : I ,
@@ -10,8 +10,8 @@ fn k_smallest_dynamic<T, I: Iterator<Item = T>>(
10
10
) -> IntoIter < T > {
11
11
let mut storage = Vec :: new ( ) ;
12
12
storage. resize_with ( k, MaybeUninit :: uninit) ;
13
- let num_elements = capped_heapsort ( iter, & mut storage, order) ;
14
- storage. truncate ( num_elements ) ;
13
+ let Range { end , .. } = capped_heapsort ( iter, & mut storage, order) ;
14
+ storage. truncate ( end ) ;
15
15
let initialized: Vec < _ > = unsafe { transmute ( storage) } ;
16
16
initialized. into_iter ( )
17
17
}
@@ -61,15 +61,22 @@ where
61
61
}
62
62
63
63
/// Consumes a given iterator, leaving the minimum elements in the provided storage in **ascending** order.
64
- /// Returns the number of elements processed, up to the size of the storage
64
+ /// Returns the range of initialized elements
65
65
fn capped_heapsort < T , I : Iterator < Item = T > > (
66
66
iter : I ,
67
67
storage : & mut [ MaybeUninit < T > ] ,
68
68
order : impl Fn ( & T , & T ) -> Ordering ,
69
- ) -> usize {
70
- let mut heap = MaxHeap :: new ( & mut storage[ ..] , order) ;
71
- heap. extend ( iter) ;
72
- heap. total_sort ( )
69
+ ) -> Range < usize > {
70
+ if storage. is_empty ( ) {
71
+ return 0 ..0 ;
72
+ }
73
+ let mut heap = MaxHeap :: from_iter ( storage, order, iter) ;
74
+
75
+ let valid_elements = 0 ..heap. len ;
76
+ while heap. len > 0 {
77
+ heap. pop ( ) ;
78
+ }
79
+ valid_elements
73
80
}
74
81
75
82
/// An efficient heapsort requires that the heap ordering is the inverse of the desired sort order
@@ -83,6 +90,7 @@ fn capped_heapsort<T, I: Iterator<Item = T>>(
83
90
struct MaxHeap < ' a , T , C > {
84
91
// It may be not be possible to shrink the storage for smaller sequencess
85
92
// so manually manage the initialization
93
+ // This is **assumed not to be empty**
86
94
storage : & ' a mut [ MaybeUninit < T > ] ,
87
95
comparator : C ,
88
96
// SAFETY: this must always be less or equal to the count of actually initialized elements
@@ -93,37 +101,31 @@ impl<'a, T, C> MaxHeap<'a, T, C>
93
101
where
94
102
C : Fn ( & T , & T ) -> Ordering ,
95
103
{
96
- fn extend < T : IntoIterator < Item = A > > ( & mut self , iter : T ) {
97
- let mut iter = iter. into_iter ( ) ;
98
-
99
- for initial_item in iter. by_ref ( ) . take ( self . storage . len ( ) - self . len ) {
100
- // This is the only point where the length is increased
101
- // And the element we are increasing from is always initialized
102
- self . storage [ self . len ] = MaybeUninit :: new ( initial_item) ;
103
- self . len += 1 ;
104
+ fn from_iter < I > ( storage : & ' a mut [ MaybeUninit < T > ] , comparator : C , mut iter : I ) -> Self
105
+ where
106
+ I : Iterator < Item = T > ,
107
+ {
108
+ let mut heap = Self {
109
+ storage,
110
+ comparator,
111
+ len : 0 ,
112
+ } ;
113
+ for ( i, initial_item) in iter. by_ref ( ) . take ( heap. storage . len ( ) ) . enumerate ( ) {
114
+ heap. storage [ i] = MaybeUninit :: new ( initial_item) ;
115
+ heap. len += 1 ;
104
116
}
105
117
// Filling up the storage and only afterwards rearranging to form a valid heap is slightly more efficient
106
118
// (But only by a factor of lg(k) and I'd love to hear of a usecase where that matters)
107
- self . heapify ( ) ;
119
+ heap . heapify ( ) ;
108
120
109
- if self . len == self . storage . len ( ) {
121
+ if heap . len == heap . storage . len ( ) {
110
122
// Nothing else needs done if we didn't fill the storage in the first place
111
123
// Also avoids unexpected behaviour with restartable iterators
112
124
for val in iter {
113
- let _ = self . push_pop ( val) ;
125
+ let _ = heap . push_pop ( val) ;
114
126
}
115
127
}
116
- }
117
- }
118
-
119
- impl < ' a , T , C > MaxHeap < ' a , T , C > {
120
- /// Creates an empty [`MaxHeap<T, C>`].
121
- fn new ( storage : & ' a mut [ MaybeUninit < T > ] , comparator : C ) -> Self {
122
- Self {
123
- storage,
124
- comparator,
125
- len : 0 ,
126
- }
128
+ heap
127
129
}
128
130
129
131
/// Retrieves the element at the given index.
@@ -169,9 +171,21 @@ impl<'a, T, C> MaxHeap<'a, T, C> {
169
171
}
170
172
}
171
173
174
+ /// Pop the greatest element by putting it at the back of the storage
175
+ /// shrinking the heap by 1, and reordering if needed
176
+ fn pop ( & mut self ) {
177
+ debug_assert ! ( self . len > 0 ) ;
178
+ self . storage . swap ( 0 , self . len - 1 ) ;
179
+ // Leaves the length shorter than the number of initialized elements
180
+ // so that sifting does not disturb already popped elements
181
+ self . len -= 1 ;
182
+ self . sift_down ( 0 ) ;
183
+ }
184
+
172
185
/// Insert the given element into the heap without changing its size
173
186
/// The displaced element is returned, i.e. either the input or previous max
174
187
fn push_pop ( & mut self , val : T ) -> Option < T > {
188
+ if self . compare ( self . get ( 0 ) , Some ( & val) ) == Some ( Ordering :: Greater ) {
175
189
let out = replace ( & mut self . storage [ 0 ] , MaybeUninit :: new ( val) ) ;
176
190
self . sift_down ( 0 ) ;
177
191
// SAFETY: This has been moved out of storage[0]
@@ -195,23 +209,6 @@ impl<'a, T, C> MaxHeap<'a, T, C> {
195
209
fn compare ( & self , a : Option < & T > , b : Option < & T > ) -> Option < Ordering > {
196
210
( self . comparator ) ( a?, b?) . into ( )
197
211
}
198
-
199
- /// Heapsorts the storage into **ascending** order by repeatedly popping.
200
- /// The number of elements in the heap is returned.
201
- fn total_sort ( mut self ) -> usize
202
- where
203
- C : Fn ( & T , & T ) -> Ordering ,
204
- {
205
- let original_len = self . len ;
206
- while self . len > 1 {
207
- self . storage . swap ( 0 , self . len - 1 ) ;
208
- // Leaves the length shorter than the number of initialized elements
209
- // so that sifting does not disturb already popped elements
210
- self . len -= 1 ;
211
- self . sift_down ( 0 ) ;
212
- }
213
- original_len
214
- }
215
212
}
216
213
217
214
struct Pair < K , T > ( K , T ) ;
0 commit comments