Advent of Code 2024 - Day 9Disk Fragmenter
| Problem statement | Source code | Tags: Data structures
This problem pays tribute to 2021 day 23.
Part 1
The first step is to parse the input into the same format as used in the demonstration: an array of blocks, which is -1 if empty, and the file ID otherwise.
Then we make a list of blank positions, which we can use for insertion later.
Now we can loop through each block position from the end, using the list of blank positions as a queue. Each time we find a non-empty blank, we swap it with the first blank position in the list, and remove that position from the list. We stop when the iteration reaches the start of the blocks array, or when the current block is to the left of the first blank position.
Finally calculate the checksum as specified:
Part 2
Now we want to move files as a whole, meaning we can no longer iterate blocks as discrete things. So the first step is to represent the disk as segments. My representation of the disk is a list of segments, where each segment contains a list of files followed by a free space:
Now I iterate through the segments from the end, and for each segment, I try to move each file in it to the leftmost segment that has enough free space.
Each time, I try to empty a segment by moving all of its files to the left. I go through each file, right to left (which is head to tail in the files list), and search for the leftmost segment with enough free space (s.free_size >= occ). Noticing that for each segment, files are always removed from the right and added to the right, I store the files in reverse order, so that the last file is always at the head of the list. If a suitable pos is found, I need to update the following:
- Update the
indof the file to be the new position in the target segment. - Decrease the
free_sizeof the target segment by the size of the file. - Append the file to that segment's
fileslist (which is the head of the list). - Remove the file from the current segment's
fileslist. I have aremaining_fileslist that I build up, which are the files that I can't move and should remain in the current segment. - Because any segment to the right of the current segment will never be used again, I don't need to worry about the current segment's
free_size, which is only relevant for segments waiting to receive files. This also means that I don't have to merge any fragmented space either.
Because each file knows its index, the checksum is as straightforward as part 1.