Linked lists are fundamental in embedded systems — RTOS task queues, memory pool free lists, interrupt handler chains, and message queues all use linked list structures internally. Mastering in-place linked list manipulation with O(1) space is essential when working without dynamic memory.
Problems
| # | Problem | Difficulty | Frequency |
|---|---|---|---|
| 1 | Reverse Linked List | Easy | ★★★★★ |
| 2 | Linked List Cycle | Easy | ★★★★★ |
| 3 | Delete Node in a Linked List | Easy | ★★★★★ |
| 4 | Remove Duplicates from Sorted List | Easy | ★★★★★ |
| 5 | Merge Two Sorted Lists | Easy | ★★★★★ |
| 6 | Palindrome Linked List | Easy | ★★★★★ |
| 7 | Remove Linked List Elements | Easy | ★★★★★ |
| 8 | Intersection of Two Linked Lists | Easy | ★★★★★ |
| 9 | Middle of Linked List | Easy | ★★★★★ |
| 10 | Convert Binary Number in Linked List to Integer | Easy | ★★★★★ |
| 11 | Implement Queue by Linked List | Easy | ★★★★★ |
| 12 | Reorder List | Medium | ★★★★★ |
| 13 | Delete Nth Node from End of Linked List | Medium | ★★★★★ |
| 14 | Reverse Linked List II | Medium | ★★★★★ |
| 15 | Delete Node in Circular Linked List | Medium | ★★★★★ |
| 16 | Convert Singly Linked List to BST | Medium | ★★★★★ |
| 17 | Flatten Binary Tree to Linked List | Medium | ★★★★★ |
Pattern Cheat Sheet
| Pattern | Technique | Problems |
|---|---|---|
| Pointer reversal | Iterate with prev/curr/next | Reverse List, Reverse List II, Palindrome |
| Slow/fast pointers | Detect cycles, find middle | Cycle Detection, Palindrome, Reorder |
| Dummy head node | Simplify edge cases for head changes | Merge, Remove Elements, Remove Duplicates, Delete Nth |
| Two-pointer gap | Advance one pointer ahead, walk both | Delete Nth from End, Intersection |
| Composite | Combine multiple patterns | Reorder (find middle + reverse + merge) |
Implementation
Reverse Linked List Easy ★★★★★
Problem: Given the head of a singly linked list, reverse the list and return the reversed list.
QSolution — O(n) time, O(1) space
Key Insight: You only need three pointers to reverse a linked list in a single pass. At each node, save the next pointer, reverse the current link, then advance. The previous pointer ends up at the new head.
Thought Process:
- Recursive approach would be O(n) space due to call stack — not ideal for embedded.
- Iterative: maintain
pre,cur, andnxt. At each step,cur->next = prereverses the link. - After the loop,
curis null andprepoints to the last node visited — the new head. - Edge cases: empty list or single node — return head immediately (no work needed).
Time Complexity: O(n) — single pass through all nodes. Space Complexity: O(1) — three pointers regardless of list size.
class Solution {public:ListNode* reverseList(ListNode* head) {if (!head || !head->next) return head;ListNode *pre = nullptr, *cur = head, *nxt;while (cur) {nxt = cur->next;cur->next = pre;pre = cur;cur = nxt;}return pre;}};
Embedded relevance: Reversing a linked list in-place is the building block for reordering RTOS task priority queues and reversing message buffers without extra memory allocation.
Linked List Cycle Easy ★★★★★
Problem: Given the head of a linked list, determine if the linked list has a cycle in it.
QSolution — O(n) time, O(1) space
Key Insight: Two pointers moving at different speeds will eventually meet if there is a cycle — the fast pointer "laps" the slow pointer. If there is no cycle, the fast pointer reaches null first.
Thought Process:
- Hash set approach: store visited node addresses, check for repeats — O(n) time but O(n) space. Not suitable for embedded.
- Floyd's tortoise-and-hare: slow moves 1 step, fast moves 2 steps. If a cycle exists, fast enters the cycle first and eventually catches slow from behind.
- Why do they always meet? Once both are in the cycle, the gap between them decreases by 1 each step (fast gains 1 step per iteration). They must meet within one full cycle traversal.
- Termination: if fast reaches null (or fast->next is null), no cycle exists.
Time Complexity: O(n) — fast pointer traverses at most 2n nodes total. Space Complexity: O(1) — two pointers.
class Solution {public:bool hasCycle(ListNode* head) {if (!head || !head->next) return false;ListNode *slow = head, *fast = head;while (fast && fast->next) {slow = slow->next;fast = fast->next->next;if (slow == fast) return true;}return false;}};
Embedded relevance: Cycle detection is critical in embedded systems for validating linked data structures in memory — a corrupted pointer can create a cycle that causes an infinite loop, hanging the system.
Delete Node in a Linked List Easy ★★★★★
Problem: Given only access to a node in a singly linked list (not the tail), delete that node.
QSolution — O(1) time, O(1) space
Key Insight: You cannot actually remove the given node from the list (no access to the previous node). Instead, copy the next node's value into the current node, then skip over the next node. The current node "becomes" its successor.
Thought Process:
- Normally, deletion in a singly linked list requires the previous node to re-link around the deleted node. But we only have the node itself.
- Trick: overwrite the current node's value with the next node's value, then delete the next node by pointing
node->nextpast it. - Caveat: this changes the node identity — any external pointers to the original next node now point to a freed node. Interviewers may ask about this tradeoff.
- This only works for non-tail nodes (the problem guarantees the node is not the tail).
Time Complexity: O(1) — constant-time pointer manipulation. Space Complexity: O(1) — no extra storage.
class Solution {public:void deleteNode(ListNode* node) {if (!node || !node->next) return;node->val = node->next->val;ListNode* toDelete = node->next;node->next = node->next->next;delete toDelete;}};
Embedded relevance: This copy-and-skip trick appears in intrusive linked lists used by RTOS kernels — where nodes are embedded inside task control blocks and you cannot always traverse from the head.
Remove Duplicates from Sorted List Easy ★★★★★
Problem: Given the head of a sorted linked list, delete all duplicates such that each element appears only once.
QSolution — O(n) time, O(1) space
Key Insight: Since the list is sorted, all duplicates are adjacent. Walk through comparing each node to its predecessor — when values differ, link the unique node into the result; when they match, skip the duplicate.
Thought Process:
- If the list were unsorted, we would need a hash set to track seen values — O(n) space. But sorted order guarantees duplicates cluster together.
- Use two pointers:
pretracks the last unique value seen,curscans ahead. Whencur->val != pre->val, linkcurinto the de-duplicated list. - After the loop, terminate the result list by setting
dummy->next = cur(null) to cut off any trailing duplicates. - The head never changes (first node is always unique), so no dummy head is needed for this variant.
Time Complexity: O(n) — single pass through all nodes. Space Complexity: O(1) — two pointers.
class Solution {public:ListNode* deleteDuplicates(ListNode* head) {ListNode *dummy, *pre, *cur;if (!head || !head->next) return head;dummy = pre = head;cur = head->next;while (cur) {if (pre->val != cur->val) {dummy->next = cur;dummy = cur;}pre = cur;cur = cur->next;}dummy->next = cur;return head;}};
Embedded relevance: Deduplication of sorted sensor readings is common when processing time-series data from ADCs — removing redundant samples before storing to flash or transmitting over a low-bandwidth link.
Merge Two Sorted Lists Easy ★★★★★
Problem: Given the heads of two sorted linked lists, merge them into one sorted linked list.
QSolution — O(n+m) time, O(1) space
Key Insight: A dummy head node eliminates the special case of choosing which list provides the first node. After the dummy, simply compare heads and append the smaller one — the logic is uniform throughout.
Thought Process:
- Without a dummy node, you need an
if/elseto determine which list's head becomes the merged list's head. A dummy node makes the first append identical to all subsequent appends. - At each step, compare
l1->valandl2->val, append the smaller node todummy, and advance that list's pointer. - When one list is exhausted, append the entire remainder of the other — no need to copy node by node since the tail is already sorted.
- Return
head.nextto skip the dummy node.
Time Complexity: O(n + m) — each node from both lists is visited exactly once. Space Complexity: O(1) — the dummy node is stack-allocated; no heap allocation.
class Solution {public:ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {if (!l1) return l2;if (!l2) return l1;ListNode head(0);ListNode* dummy = &head;while (l1 && l2) {if (l1->val < l2->val) {dummy->next = l1;l1 = l1->next;} else {dummy->next = l2;l2 = l2->next;}dummy = dummy->next;}dummy->next = (l1) ? l1 : l2;return head.next;}};
Embedded relevance: Merging sorted lists is the core operation in merge sort — the preferred sorting algorithm for linked data in embedded systems because it is stable, O(n log n), and requires no random access.
Palindrome Linked List Easy ★★★★★
Problem: Given the head of a singly linked list, return true if it is a palindrome.
QSolution — O(n) time, O(1) space
Key Insight: This is a composite problem that chains three linked list techniques: (1) find the middle with slow/fast pointers, (2) reverse the second half in-place, (3) compare both halves node by node.
Thought Process:
- Brute force: copy values to an array, check array palindrome — O(n) space. Fails the O(1) space constraint.
- Can we compare without extra storage? If we reverse the second half of the list, we can walk two pointers from both ends toward the middle.
- Step 1: find the middle with slow/fast. Step 2: reverse from
slowto the end. Step 3: compare the original first half and the reversed second half. - For odd-length lists, the middle node is compared against itself (always matches), so no special case is needed.
Time Complexity: O(n) — three O(n) passes (find middle, reverse, compare). Space Complexity: O(1) — in-place reversal, two comparison pointers.
class Solution {public:ListNode* reverse(ListNode* head) {ListNode *pre = nullptr, *cur = head, *nxt;while (cur) {nxt = cur->next;cur->next = pre;pre = cur;cur = nxt;}return pre;}bool isPalindrome(ListNode* head) {if (!head || !head->next) return true;ListNode *slow = head, *fast = head;while (fast && fast->next) {slow = slow->next;fast = fast->next->next;}ListNode *rev = reverse(slow);while (rev) {if (rev->val != head->val) return false;rev = rev->next;head = head->next;}return true;}};
Embedded relevance: The pattern of splitting a data stream, processing each half independently, and comparing results mirrors how redundant sensor channels are validated — process both readings and compare for consistency.
Remove Linked List Elements Easy ★★★★★
Problem: Given the head of a linked list and an integer val, remove all nodes with that value.
QSolution — O(n) time, O(1) space
Key Insight: A dummy node before the head handles the tricky case where the head itself needs to be removed. Without a dummy, you would need separate logic for removing head nodes vs. interior nodes.
Thought Process:
- If the head matches
val, the head must change. This is awkward to handle without a dummy node. - With a dummy: iterate through the list with
preandcur. Ifcur->val == val, skip it. Otherwise, link it into the result viapre->next = cur. - After the loop, set
pre->next = nullptrto terminate the result list (handles trailing matches). - Return
dummy.next— which is the new head after all removals.
Time Complexity: O(n) — single pass through all nodes. Space Complexity: O(1) — dummy node on the stack, two pointers.
class Solution {public:ListNode* removeElements(ListNode* head, int val) {ListNode dummy;dummy.next = head;ListNode *pre = &dummy, *cur = head;while (cur) {if (cur->val == val) {cur = cur->next;} else {pre->next = cur;cur = cur->next;pre = pre->next;}}pre->next = cur;return dummy.next;}};
Embedded relevance: Removing specific elements from a linked list mirrors how RTOS event queues filter out cancelled or expired events — walking the queue and unlinking matching entries without disturbing the rest.
Intersection of Two Linked Lists Easy ★★★★★
Problem: Given the heads of two singly linked lists, return the node at which they intersect, or null.
QSolution — O(n) time, O(1) space
Key Insight: If two lists intersect, they share a common tail. The intersection point is missed when traversing both from their heads because the lists have different lengths. Advance the longer list by the length difference so both pointers are equidistant from the intersection.
Thought Process:
- Hash set approach: store all nodes from list A, walk list B checking for matches — O(n + m) time but O(n) space.
- Length-based approach: count both lengths, advance the longer list by
|lenA - lenB|steps, then walk both in lockstep. The first node where they point to the same address is the intersection. - Compare by pointer identity (
longer == shorter), not by value — two nodes can have the same value without being the same node. - If no intersection exists, both pointers reach null simultaneously.
Time Complexity: O(n + m) — two passes to count lengths, one pass to find intersection. Space Complexity: O(1) — four pointers and two counters.
class Solution {public:ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {if (!headA || !headB) return nullptr;int lenA = 0, lenB = 0;ListNode *A = headA, *B = headB;while (A) { lenA++; A = A->next; }while (B) { lenB++; B = B->next; }int diff = abs(lenA - lenB);ListNode *longer = lenA > lenB ? headA : headB;ListNode *shorter = lenA > lenB ? headB : headA;while (diff--) longer = longer->next;while (longer && shorter) {if (longer == shorter) return longer;longer = longer->next;shorter = shorter->next;}return nullptr;}};
Embedded relevance: Detecting shared data structure nodes is important in memory pool debugging — finding where two allocation chains merge can reveal double-free bugs or unintended aliasing.
Middle of the Linked List Easy ★★★★★
Problem: Given the head of a singly linked list, return the middle node. If two middle nodes, return the second one.
QSolution — O(n) time, O(1) space
Key Insight: You don't need to know the length first. The slow/fast pointer technique finds the middle in a single pass — no counting, no second traversal.
Thought Process:
- Naive approach: traverse once to count length, then traverse again to length/2. Two passes.
- Better: use two pointers. Slow moves 1 step, fast moves 2 steps. When fast reaches the end, slow is at the middle.
- Why does slow land on the second middle for even-length lists? Because both pointers start at head. If fast started at head->next, slow would land on the first middle instead.
- The termination condition
fast != NULL && fast->next != NULLhandles both odd and even lengths cleanly.
Time Complexity: O(n) — single pass (fast pointer traverses the full list). Space Complexity: O(1) — two pointers.
class Solution {public:ListNode* middleNode(ListNode* head) {ListNode* slow = head;ListNode* fast = head;while (fast != NULL && fast->next != NULL) {slow = slow->next;fast = fast->next->next;}return slow;}};
Embedded relevance: The slow/fast pointer pattern is used in circular buffer overflow detection — checking if a producer pointer has lapped a consumer pointer without maintaining a separate count variable.
Convert Binary Number in a Linked List to Integer Easy ★★★★★
Problem: Given a linked list where each node holds a 0 or 1 (MSB at head), return the decimal value.
QSolution — O(n) time, O(1) space
Key Insight: This is a shift register in software. As you traverse the list, each new bit arrives from the MSB side. Left-shift the accumulator and OR in the new bit — exactly what a serial-in parallel-out (SIPO) shift register does in hardware.
Thought Process:
- The list gives us bits MSB-first. We need to build the number incrementally.
- At each node: the existing accumulated value needs to "make room" for the new bit by shifting left, then the new bit is placed in the lowest position via OR.
num = (num << 1) | head->val— this is the core operation. It works for any starting value of num (initialized to 0).- No need to know the list length in advance — the shift-and-OR naturally handles any length.
Time Complexity: O(n) — single traversal. Space Complexity: O(1) — single integer accumulator.
class Solution {public:int getDecimalValue(ListNode* head) {int num = 0;while (head != nullptr) {num = (num << 1) | head->val;head = head->next;}return num;}};
Embedded relevance: This is exactly how a SIPO shift register works — bits arrive one at a time (MSB first), shifted into a register that accumulates the full word. Used in SPI receive paths and serial protocol decoders.
Implement Queue by Linked List Easy ★★★★★
Problem: Implement a queue (FIFO) data structure using a singly linked list. Support enqueue (add to back), dequeue (remove from front), peek (view front), and isEmpty operations.
QSolution — O(1) per operation, O(n) space
Key Insight: A singly linked list naturally supports O(1) removal from the front (head) and O(1) insertion at the back — if you maintain a tail pointer. Without a tail pointer, enqueue would be O(n) because you would have to walk to the end every time.
Thought Process:
- Queue needs FIFO: enqueue at the back, dequeue from the front.
- Singly linked list gives O(1) removal from head (just advance head). But insertion at the tail is O(n) without a tail pointer.
- Solution: maintain both
headandtailpointers. Enqueue links a new node attail->nextand advancestail. Dequeue advanceshead. - Edge case: when the queue becomes empty after a dequeue, reset
tailto null as well. When enqueueing into an empty queue, bothheadandtailpoint to the new node.
Time Complexity: O(1) per operation — enqueue, dequeue, peek, and isEmpty are all constant time. Space Complexity: O(n) — one node per element in the queue.
struct ListNode {int val;ListNode* next;ListNode(int x) : val(x), next(nullptr) {}};class Queue {ListNode* head;ListNode* tail;public:Queue() : head(nullptr), tail(nullptr) {}void enqueue(int val) {ListNode* node = new ListNode(val);if (!tail) {head = tail = node;} else {tail->next = node;tail = node;}}int dequeue() {if (!head) return -1; // or throwint val = head->val;ListNode* tmp = head;head = head->next;if (!head) tail = nullptr;delete tmp;return val;}int peek() {return head ? head->val : -1;}bool isEmpty() {return head == nullptr;}};
Embedded relevance: Linked-list queues are the backbone of RTOS message passing — FreeRTOS xQueueSend/xQueueReceive and Zephyr k_msgq use similar structures internally for inter-task communication with O(1) enqueue/dequeue.
Reorder List Medium ★★★★★
Problem: Given a singly linked list L0→L1→...→Ln, reorder it to L0→Ln→L1→Ln-1→... in-place.
QSolution — O(n) time, O(1) space
Key Insight: This problem decomposes into three sub-problems you already know: (1) find the middle, (2) reverse the second half, (3) interleave-merge the two halves. Each step uses a fundamental linked list technique.
Thought Process:
- Brute force: copy values to an array, rearrange, copy back — O(n) space. We want O(1).
- Observation: the reordered list interleaves the first half with the reversed second half. So split, reverse, and merge.
- Step 1: slow/fast to find the middle. Step 2: reverse from
slow->nextonward. Step 3: alternate nodes from both halves. - Cut the first half from the second by setting
slow->next = NULLafter finding the middle. This prevents an infinite loop during the merge.
Time Complexity: O(n) — three linear passes (find middle, reverse, merge). Space Complexity: O(1) — all operations are in-place.
class Solution {public:void reorderList(ListNode* head) {if (!head) return;// Step 1: Find middleListNode *slow = head, *fast = head;while (fast->next && fast->next->next) {slow = slow->next;fast = fast->next->next;}// Step 2: Reverse second halfListNode *prev = NULL, *cur = slow->next, *save;while (cur) {save = cur->next;cur->next = prev;prev = cur;cur = save;}slow->next = NULL;// Step 3: Interleave mergeListNode *head2 = prev;while (head2) {save = head->next;head->next = head2;head = head2;head2 = save;}}};
Embedded relevance: Interleaving two data streams is common in embedded audio processing — alternating left/right channel samples into a single interleaved buffer for I2S DMA transfer.
Delete Nth Node From End of Linked List Medium ★★★★★
Problem: Given the head of a linked list, remove the nth node from the end and return its head.
QSolution — O(n) time, O(1) space
Key Insight: Create a gap of exactly n+1 nodes between two pointers. When the leading pointer reaches null, the trailing pointer is right before the node to delete. A sentinel node handles the edge case of deleting the head.
Thought Process:
- Two-pass approach: count the length L, then delete the (L - n + 1)th node from the start. Simple but requires two traversals.
- One-pass: advance
secondpointer n+1 steps ahead offirst. Then move both at the same pace. Whensecondreaches null,firstis at the predecessor of the target. - A sentinel (dummy) node before head simplifies the case where n equals the list length (deleting the head itself) —
firstends up at the sentinel, andfirst->next = first->next->nextremoves the original head. - Guard: if n exceeds the list length,
secondbecomes null during the initial advance — return the original head unchanged.
Time Complexity: O(n) — single pass (the second pointer traverses the full list). Space Complexity: O(1) — two pointers and a sentinel.
class Solution {public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode sentinel;sentinel.next = head;ListNode *first = &sentinel, *second = &sentinel;for (int i = 0; i <= n; i++) {if (!second) return head;second = second->next;}while (second) {first = first->next;second = second->next;}first->next = first->next->next;return sentinel.next;}};
Embedded relevance: The two-pointer gap technique mirrors how ring buffer consumers track a "lag" behind the producer — maintaining a fixed offset to ensure a certain number of items remain buffered.
Reverse Linked List II Medium ★★★★★
Problem: Given the head of a singly linked list and two integers left and right, reverse the nodes from position left to position right.
QSolution — O(n) time, O(1) space
Key Insight: Partial reversal is the same as full reversal but with boundaries. Navigate to the node before left, reverse only the sublist from left to right, then reconnect both ends. A dummy node simplifies the case where left = 1.
Thought Process:
- First, locate the node just before position
left(call itpre). Also locate the node at positionleft(call itcur) and the node just after positionright(call ittarget). - Reverse the sublist from
curtotargetusing the standard three-pointer reversal, stopping whencur == target. - After reversal,
pre->nextpoints to the new sublist head (the oldrightnode), and the oldleftnode's next points totarget(the node after the reversed section). - A dummy node before head handles
left = 1— otherwiseprewould be null.
Time Complexity: O(n) — at most two passes (navigate to position + reverse sublist). Space Complexity: O(1) — constant number of pointers.
class Solution {public:ListNode* reverseNode(ListNode* head, ListNode* target) {ListNode *pre = nullptr, *cur = head, *nxt;while (cur != target) {nxt = cur->next;cur->next = pre;pre = cur;cur = nxt;}head->next = target;return pre;}ListNode* reverseBetween(ListNode* head, int m, int n) {ListNode dummy;dummy.next = head;if (!head) return head;ListNode *pre = &dummy, *cur = head, *target = head;while (--m) { pre = cur; cur = cur->next; }while (n--) { target = target->next; }pre->next = reverseNode(cur, target);return dummy.next;}};
Embedded relevance: Partial list reversal is analogous to in-place byte reversal within a specific field of a protocol frame — reversing endianness of a multi-byte value without copying the entire frame.
Delete Node in Circular Linked List Medium ★★★★★
Problem: Given a pointer to a node in a circular singly linked list, delete that node. You are only given the pointer to the node to delete (not the head of the list).
QSolution — O(n) time, O(1) space
Key Insight: In a circular list, you can always reach any node's predecessor by walking forward until you loop back around. Unlike the "delete node" trick for non-circular lists (copy-and-skip), here we can find the previous node and properly unlink the target.
Thought Process:
- Copy-and-skip (like the standard "delete node" problem) works if the target is not the only node. But in a circular list we can do better — we can find the predecessor.
- Start at the target node and walk forward through the circular list until
runner->next == node. Nowrunneris the predecessor. - Set
runner->next = node->nextto unlink the target. If the target is the only node in the list (node->next == node), the list becomes empty. - Special case: if someone holds a "head" pointer to the deleted node, they need to update it. This problem only asks us to delete, not maintain a head reference.
Time Complexity: O(n) — worst case traverses the entire circular list to find the predecessor. Space Complexity: O(1) — one traversal pointer.
void deleteNode(ListNode* node) {if (!node) return;// Single node in circular listif (node->next == node) {delete node;return;}// Find predecessor by walking the circleListNode* runner = node;while (runner->next != node) {runner = runner->next;}// Unlink and deleterunner->next = node->next;delete node;}
Embedded relevance: Circular linked lists are used in round-robin schedulers and timer wheel implementations. Deleting a node from a circular list mirrors cancelling a scheduled timer or removing a task from a round-robin rotation.
Convert Singly Linked List to BST Medium ★★★★★
Problem: Given the head of a sorted singly linked list, convert it to a height-balanced binary search tree (BST).
QSolution — O(n) time, O(log n) space
Key Insight: A sorted list already provides BST in-order traversal order. Instead of finding the middle element repeatedly (which is O(n) per level), simulate in-order traversal: advance a global list pointer as you build the tree bottom-up. The list pointer naturally visits nodes in sorted order.
Thought Process:
- Array approach: copy to array, recursively pick the middle element as root — O(n) extra space for the array.
- Find-middle approach: use slow/fast to find the middle of the list at each recursion level. But finding the middle is O(n) per call, giving O(n log n) total.
- Optimal: count the list length first (O(n)). Then recurse with
buildBST(start, end). At each leaf level, consume the current list node and advance the pointer. This simulates in-order traversal — left subtree consumes the first half of nodes, root consumes the middle, right subtree consumes the rest. - The recursion depth is O(log n) for a balanced tree, which is the space for the call stack.
Time Complexity: O(n) — each list node is visited exactly once during the in-order simulation. Space Complexity: O(log n) — recursion stack depth for a balanced tree.
class Solution {ListNode* cur;TreeNode* buildBST(int start, int end) {if (start > end) return nullptr;int mid = start + (end - start) / 2;TreeNode* left = buildBST(start, mid - 1);TreeNode* root = new TreeNode(cur->val);cur = cur->next;root->left = left;root->right = buildBST(mid + 1, end);return root;}public:TreeNode* sortedListToBST(ListNode* head) {cur = head;int len = 0;ListNode* tmp = head;while (tmp) { len++; tmp = tmp->next; }return buildBST(0, len - 1);}};
Embedded relevance: Building balanced search trees from sorted data is useful for constructing efficient lookup tables at boot time — for example, converting a sorted configuration key list into a BST for O(log n) runtime lookups instead of O(n) linear search.
Flatten Binary Tree to Linked List Medium ★★★★★
Problem: Given the root of a binary tree, flatten the tree into a linked list in-place following pre-order traversal.
QSolution — O(n) time, O(1) space
Key Insight: For each node with a left child, find the rightmost node of the left subtree and thread it to the current node's right child. Then move the entire left subtree to the right side. This is a variant of Morris traversal — no stack or recursion needed.
Thought Process:
- Recursive approach: flatten left, flatten right, attach left's tail to right subtree — O(n) time but O(h) stack space.
- Stack-based iterative: pre-order traversal with a stack, re-linking as you go — O(n) time, O(h) space.
- Morris-style O(1) space: for each node with a left child, find the left subtree's rightmost node (the in-order predecessor). Connect its right pointer to the current node's right child. Then move the left subtree to the right. This "threads" the tree without extra space.
- After threading, set
root->left = NULLand advance toroot->right. Repeat until done.
Time Complexity: O(n) — each node is visited at most twice (once to find the predecessor, once to advance). Space Complexity: O(1) — no stack, no recursion, just pointer manipulation.
class Solution {public:void flatten(TreeNode* root) {if (root == NULL) return;while (root) {if (root->left) {TreeNode* pre = root->left;while (pre->right) pre = pre->right;pre->right = root->right;root->right = root->left;root->left = NULL;}root = root->right;}}};
Embedded relevance: Flattening a tree into a linked list is useful for serializing hierarchical device tree structures into a linear format for DMA transfer or flash storage — converting a tree representation into a sequential byte stream.
