Implement a memory allocator with malloc/free
Company: OpenAI
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates understanding of memory allocator concepts including malloc/free semantics, contiguous allocation, fragmentation handling, coalescing of adjacent free blocks, pointer validation, and data-structure choices for efficient allocation.
Constraints
- 1 <= totalSize <= 10^9
- 1 <= len(operations) <= 2 * 10^5
- For ('malloc', size): 1 <= size <= 10^9
- Pointers passed to free may be invalid or already freed
- Memory blocks returned by malloc are always contiguous and non-overlapping
Examples
Input: (10, [('malloc', 3), ('malloc', 4), ('free', 0), ('malloc', 2), ('free', 3), ('malloc', 5)])
Expected Output: [0, 3, True, 0, True, 2]
Explanation: Alloc 3 at 0, alloc 4 at 3, free 0..2, alloc 2 reuses 0..1, free 3..6 which coalesces with remaining free into 2..9, alloc 5 at 2.
Input: (5, [('malloc', 2), ('malloc', 3), ('malloc', 1), ('free', 0), ('free', 0), ('malloc', 1)])
Expected Output: [0, 2, -1, True, False, 0]
Explanation: After allocating 2 and 3 bytes, no space remains. Freeing pointer 0 succeeds once, then fails on double-free. Final malloc(1) uses freed space at 0.
Input: (8, [('malloc', 3), ('malloc', 3), ('malloc', 2), ('free', 3), ('free', 0), ('malloc', 6)])
Expected Output: [0, 3, 6, True, True, 0]
Explanation: Freeing 3..5 and 0..2 coalesces into a 0..5 free block, allowing malloc(6) at 0.
Input: (3, [('free', 1), ('malloc', 3), ('free', 0), ('malloc', 1)])
Expected Output: [False, 0, True, 0]
Explanation: Freeing an unknown pointer fails. Then full allocation succeeds, freeing it succeeds, and malloc(1) reuses address 0.
Input: (6, [('malloc', 1), ('malloc', 1), ('malloc', 1), ('free', 1), ('malloc', 2), ('free', 0), ('free', 2), ('free', 3), ('malloc', 6)])
Expected Output: [0, 1, 2, True, 3, True, True, True, 0]
Explanation: Demonstrates fragmentation, reuse, and full coalescing back into a single 0..5 free block enabling malloc(6).
Solution
def solution(totalSize, operations):
outputs = []
# Free intervals as [start, end) sorted by start
free_list = []
if totalSize > 0:
free_list.append([0, totalSize])
allocated = {}
def lower_bound_free(x):
lo, hi = 0, len(free_list)
while lo < hi:
mid = (lo + hi) // 2
if free_list[mid][0] < x:
lo = mid + 1
else:
hi = mid
return lo
for op, arg in operations:
if op == 'malloc':
size = arg
ptr = -1
for i in range(len(free_list)):
s, e = free_list[i]
if e - s >= size:
ptr = s
if e - s == size:
del free_list[i]
else:
free_list[i][0] = s + size
allocated[ptr] = size
break
outputs.append(ptr)
else: # 'free'
ptr = arg
if ptr not in allocated:
outputs.append(False)
continue
size = allocated.pop(ptr)
ns, ne = ptr, ptr + size
i = lower_bound_free(ns)
merged_left = i > 0 and free_list[i - 1][1] == ns
merged_right = i < len(free_list) and ne == free_list[i][0]
if merged_left and merged_right:
# merge three intervals: left, new, right
free_list[i - 1][1] = free_list[i][1]
del free_list[i]
elif merged_left:
free_list[i - 1][1] = ne
elif merged_right:
free_list[i][0] = ns
else:
free_list.insert(i, [ns, ne])
outputs.append(True)
return outputs
Time complexity: Expected O(log n) per operation (treap with augmented subtree max), where n is the number of free blocks; worst-case O(n) is possible but unlikely with randomized priorities.. Space complexity: O(n) for storing free blocks in the treap plus O(a) for allocated blocks, where a is the number of currently allocated blocks..