9
9
//!
10
10
//! We build 10 [min heaps](https://en.wikipedia.org/wiki/Heap_(data_structure)) in an array to
11
11
//! store the free space offsets. The index of the array implicitly stores the size of the
12
- //! free block.
12
+ //! free block. The heaps are implemented as a simple revered `vec`. Most frequently
13
+ //! items are added directly to the top of the heap, so this is faster than a real heap.
13
14
//!
14
15
//! When moving a file to a free block, the corresponding heap is popped and then any leftover
15
16
//! space is pushed back to the heap at a smaller index. The heap at index zero is not used
16
17
//! but makes the indexing easier.
17
- use crate :: util:: heap:: * ;
18
18
19
19
/// [Triangular numbers](https://en.wikipedia.org/wiki/Triangular_number) offset by two.
20
20
/// Files can be a max size of 9 so we only need the first 10 values, including zero to make
@@ -63,21 +63,28 @@ pub fn part1(disk: &[usize]) -> usize {
63
63
checksum
64
64
}
65
65
66
+ #[ allow( clippy:: needless_range_loop) ]
66
67
pub fn part2 ( disk : & [ usize ] ) -> usize {
67
68
let mut block = 0 ;
68
69
let mut checksum = 0 ;
69
- let mut free: Vec < _ > = ( 0 ..10 ) . map ( |_| MinHeap :: with_capacity ( 1_000 ) ) . collect ( ) ;
70
+ let mut free: Vec < _ > = ( 0 ..10 ) . map ( |_| Vec :: with_capacity ( 1_000 ) ) . collect ( ) ;
70
71
71
72
// Build a min-heap (leftmost free block first) where the size of each block is
72
73
// implicit in the index of the array.
73
74
for ( index, & size) in disk. iter ( ) . enumerate ( ) {
74
75
if index % 2 == 1 && size > 0 {
75
- free[ size] . push ( block, ( ) ) ;
76
+ free[ size] . push ( block) ;
76
77
}
77
78
78
79
block += size;
79
80
}
80
81
82
+ // Add sentinel value and reverse vecs so that smallest blocks are last.
83
+ for i in 0 ..10 {
84
+ free[ i] . push ( block) ;
85
+ free[ i] . reverse ( ) ;
86
+ }
87
+
81
88
for ( index, & size) in disk. iter ( ) . enumerate ( ) . rev ( ) {
82
89
block -= size;
83
90
@@ -90,25 +97,25 @@ pub fn part2(disk: &[usize]) -> usize {
90
97
let mut next_block = block;
91
98
let mut next_index = usize:: MAX ;
92
99
93
- #[ allow( clippy:: needless_range_loop) ]
94
100
for i in size..free. len ( ) {
95
- if let Some ( ( & first, ( ) ) ) = free[ i] . peek ( ) {
96
- if first < next_block {
97
- next_block = first;
98
- next_index = i;
99
- }
101
+ let top = free[ i] . len ( ) - 1 ;
102
+ let first = free[ i] [ top] ;
103
+
104
+ if first < next_block {
105
+ next_block = first;
106
+ next_index = i;
100
107
}
101
108
}
102
109
103
110
// We can make smaller free block from bigger blocks but not the other way around.
104
111
// As an optimization if all blocks of the biggest size are after our position then
105
112
// we can ignore them.
106
113
if !free. is_empty ( ) {
107
- let last = free. len ( ) - 1 ;
108
- if let Some ( ( & first , ( ) ) ) = free[ last ] . peek ( ) {
109
- if first > block {
110
- free . pop ( ) ;
111
- }
114
+ let biggest = free. len ( ) - 1 ;
115
+ let top = free[ biggest ] . len ( ) - 1 ;
116
+
117
+ if free [ biggest ] [ top ] > block {
118
+ free . pop ( ) ;
112
119
}
113
120
}
114
121
@@ -120,8 +127,20 @@ pub fn part2(disk: &[usize]) -> usize {
120
127
// If we used a free block, remove then add back any leftover space.
121
128
if next_index != usize:: MAX {
122
129
free[ next_index] . pop ( ) ;
123
- if size < next_index {
124
- free[ next_index - size] . push ( next_block + size, ( ) ) ;
130
+
131
+ // Insert the new smaller block into the correct location.
132
+ // Most frequently this is directly at the end of the vector so even though this
133
+ // is technically `O(n)`, in practice it's faster than a real heap.
134
+ let to = next_index - size;
135
+ if to > 0 {
136
+ let mut i = free[ to] . len ( ) ;
137
+ let value = next_block + size;
138
+
139
+ while free[ to] [ i - 1 ] < value {
140
+ i -= 1 ;
141
+ }
142
+
143
+ free[ to] . insert ( i, value) ;
125
144
}
126
145
}
127
146
}
0 commit comments