CodingPhone, Onsite Software Engineer, Machine Learning Engineer — reported March 2026 — medium frequency
Create a memory-manager class that controls a single contiguous address range. It should offer allocation and release behavior comparable to C's malloc() and free() operations.
Your allocator must:
Build a MemoryAllocator class exposing the interface below.
def __init__(self, total_capacity: int)
total_capacity gives the total number of bytes or abstract memory units managed by the allocator.allocate(size: int) -> intReserve one contiguous region containing size units.
Parameters:
size: The requested amount; it must be greater than zero.Returns:
Behavior:
Errors:
size is zero or negative.free(address: int, size: int) -> NoneReturn an earlier allocated region to the free memory pool.
Parameters:
address: The first address in the region being released.size: The region's length.Behavior:
Errors:
address lies outside the managed address range.size is invalid.Use a doubly linked list of free regions to implement the allocator.
Each list node represents one currently available range and contains:
start: The first address in the free range.size: The number of free units in that range.next: A link to the following free-range node.prev: A link to the preceding free-range node.Important: Keep the free list sorted by starting address. This ordering makes it possible to identify and combine adjacent ranges during free().
start forward by the allocated length and reduce its size accordingly.When inserting a released range, account for these four possibilities:
# Create an allocator containing 80 units.
allocator = MemoryAllocator(80)
# Reserve 15 units.
a = allocator.allocate(15) # Returns 0
# Memory: [Allocated(0-14)] [Free(15-79)]
# Reserve 25 units.
b = allocator.allocate(25) # Returns 15
# Memory: [Allocated(0-14)] [Allocated(15-39)] [Free(40-79)]
# Reserve 20 units.
c = allocator.allocate(20) # Returns 40
# Memory: [Allocated(0-14)] [Allocated(15-39)] [Allocated(40-59)] [Free(60-79)]
# Release the middle allocation.
allocator.free(15, 25)
# Memory: [Allocated(0-14)] [Free(15-39)] [Allocated(40-59)] [Free(60-79)]
# This request fits at the beginning of the first available range.
d = allocator.allocate(18) # Returns 15
# Memory: [Allocated(0-14)] [Allocated(15-32)] [Free(33-39)] [Allocated(40-59)] [Free(60-79)]
# Release the first allocation.
allocator.free(0, 15)
# Memory: [Free(0-14)] [Allocated(15-32)] [Free(33-39)] [Allocated(40-59)] [Free(60-79)]
# Releasing the second allocation joins the free ranges on both sides.
allocator.free(15, 18)
# Memory: [Free(0-39)] [Allocated(40-59)] [Free(60-79)]
The final merge occurs because the released range [15-32] touches [0-14] on its left and [33-39] on its right.
Once the basic allocator is complete, be ready to explain its performance and possible alternatives.
n representing the number of free-list nodes.
m is the number of free ranges.External fragmentation: Repeated operations can divide available memory into many small pieces.
Linear search: First-fit allocation may inspect every free node.
No compaction: The allocator does not move live allocations to make one larger free range.
Reducing Fragmentation:
Segregated free lists: Keep separate lists for distinct size categories.
Best-fit selection: Choose the smallest free range that can hold the request rather than the first suitable range.
Buddy allocation: Divide memory into blocks whose sizes are powers of two.
Improving Time Complexity:
Balanced binary search trees: Index free ranges by both address and size.
ptmalloc2 use red-black trees for some free-block management.Bitmap with an index: Represent fixed-size units with bits and maintain an index over available units.
Achieving Constant Space Complexity:
To use O(1) auxiliary space, place allocator metadata inside the managed memory itself.
Implicit free list: Store each block's length and occupancy flag in a header located at the block's beginning.
malloc implementations.Boundary tags: Pair the implicit-list headers with size information at each block's end.
2 * sizeof(size_t) bytes of metadata per block.Be prepared to discuss the following extensions:
free() requests can be detected?realloc() for resizing an existing allocation?Include tests for: