# #05 — Message Queue Partitioning ## Problem A communication subsystem maintains a singly linked intrusive queue of outgoing messages. Each message contains a transmission identifier, a retry flag, and a pointer to the next message: ```cpp struct Message { uint32_t id; bool retry; Message* next; }; ``` After a transmission attempt, some messages may need to be retried. Partition the original queue into two separate queues: - the ready queue, containing messages that do not require another transmission attempt; - the retry queue, containing messages marked for retry. The relative order of messages must be preserved in both queues. ### Requirements - no dynamic memory allocation; - no copying of messages; - reuse the existing list nodes; - preserve the original order in both resulting queues; - process the queue in `O(n)` time. ## Example ### Input ```text A -> B -> C -> D ``` ```text A: retry = false B: retry = true C: retry = false D: retry = true ``` ### Result Ready queue ```text A -> C ``` Retry queue ```text B -> D ``` The original queue is consumed during the operation, and every message must belong to exactly one of the two resulting queues. --- # Analysis At first glance, this looks like another linked list interview problem. Traverse the list. Check a flag. Split the nodes into two lists. Complexity: **O(n)**. Simple. Except this is one of those rare cases where the interview version is surprisingly close to a real engineering task. The interesting part is not the algorithm. The interesting part is what the algorithm is actually modifying. --- ## This Is Not About Two Lists Each node already exists. ```cpp struct Message { uint32_t id; bool retry; Message* next; }; ``` No objects are created. No objects are destroyed. No messages are copied. Only ownership changes. The original outgoing queue disappears and every message becomes part of exactly one new queue. That small detail changes the entire nature of the problem. --- ## The Real Challenge The boolean itself is trivial. ```cpp retry == true ``` is simply a classification. The difficult part is maintaining the integrity of two intrusive queues while consuming a third one. Every processed node must satisfy one invariant: - belong to exactly one queue; - never be lost; - never appear twice; - never keep stale links into the original list. Most bugs are not caused by the condition. They are caused by pointer manipulation. --- ## Why Saving `next` Matters The same pointer is used for two completely different purposes. During traversal: ```text current -> next ``` is how we reach the remaining nodes. After insertion into a new queue: ```text current -> next ``` becomes part of another list. If the original `next` pointer is overwritten before it is saved, the remainder of the queue is simply lost. This is one of the classic pitfalls of intrusive containers. --- ## Stable Partition The requirements also say: > preserve order That sounds minor. It isn't. Appending to the head would produce: ```text D -> B ``` instead of ```text B -> D ``` The algorithm therefore performs a **stable partition**, preserving FIFO order in both resulting queues. In a communication subsystem this is often essential because later messages may depend on earlier ones. --- ## Why No Allocation? The requirement ```text no allocation ``` isn't there to make the problem harder. It reflects reality. Communication stacks, embedded systems and real-time software often avoid dynamic allocation while processing packets or messages. The messages already exist. Only their position inside processing queues changes. --- ## Hidden Engineering Questions The implementation itself is small. The engineering questions are not. For example: - Who owns the original queue after partitioning? - Can another thread append messages during the operation? - Can an interrupt modify the queue? - What happens if the queue is already corrupted? - Can a message belong to multiple intrusive containers? - Should retry count also be updated? - Is there exponential backoff before retrying? None of these appear in the problem statement. All of them appear in production systems. --- ## What This Problem Actually Tests Unlike many linked list exercises, this one evaluates something genuinely useful. It tests whether a developer can safely manipulate ownership using pointers while preserving structural invariants. The algorithm itself is almost secondary. Correctness is everything. --- ## Key Takeaway This is one of the few interview-style linked list problems that has a direct equivalent in production software. Not because splitting a list is inherently interesting. But because communication stacks, schedulers, networking software and embedded systems continuously reorganize intrusive queues exactly like this. The interview version removes most of the surrounding system. The engineering version adds ownership, invariants, concurrency and failure handling. The pointer operations remain almost identical. The responsibility does not. --- ## Project Perspective > Exists in real engineering? **Yes. Frequently.** > Exists in interview form? **Yes.** One of the rare cases where the interview problem remains close to its real-world counterpart.