Welcome back. In our previous lessons, we've focused on building a decision framework to select the right algorithm for array and ordered-data problems. We saw how to use a problem's keywords and constraints to choose between patterns like hashing, sorting, or binary search.
Today, we'll extend this systematic thinking to problems involving linked data structures: linked lists, stacks, and trees. While you've encountered the basic operations for these structures in earlier modules, solving Medium-level interview problems requires a deeper insight. The key is to move beyond just traversal and identify the specific state you need to preserve or pass along at each step of your algorithm. This lesson is all about learning to define that state, which is the crucial bridge between understanding a problem and implementing a correct and efficient solution.
What is "State" in Traversal and Pointer Movement?
In the context of algorithms, "state" is the essential information your algorithm needs to maintain at any given point to make its next decision correctly.
- For a simple array loop, the state might just be the index
i. - For linked structures, the state is often more complex than just a
currentpointer. It's the collection of variables that captures the full context of your traversal.
Let's explore how defining this state is the key to unlocking solutions for various patterns.
State in Linked Lists
A linked list's non-contiguous nature means all operations are based on pointer manipulation. The state you maintain determines what you can achieve. A great way to refresh these concepts is through the "Linked Lists for Technical Interviews" course on freeCodeCamp.org.
Linked Lists for Technical Interviews - Full Course
This video provides an excellent foundation. Let's start with a quick review of the two fundamental traversal methods and the classic in-place reversal problem.
First, watch the segment on traversing a linked list iteratively and recursively. Notice how the "state" in the iterative solution is just the current pointer, while in the recursive solution, the state is implicitly managed by the call stack. Next, focus on the section explaining how to reverse a list. Pay close attention to the iterative solution. Here, the state isn't just current anymore. To reverse the pointers, you must track (previous, current, next). The previous pointer is the critical piece of state that allows you to "rewire" the list.
The reversal problem is a perfect first example. You can't solve it by only knowing where you are (current). You must also know where you came from (previous) to redirect the pointer.
Another classic linked list pattern is cycle detection, which uses a different kind of state.
LeetCode was HARD until I Learned these 15 Patterns
The "Fast & Slow Pointers" pattern, also known as the Tortoise and Hare algorithm, is a prime example of defining a state based on the relative positions of pointers. Please read the section on this pattern.
In the article, find section 4. Fast & Slow Pointers. Read the explanation of how two pointers moving at different speeds can detect a cycle.

In this case, the state is the tuple (slow, fast). The algorithm's logic depends entirely on the relationship between these two pointers at each step.
State in Trees
Trees, with their branching structure, introduce even more interesting ways to manage state during traversal. You've already seen basic DFS and BFS.

For more complex problems, we often need to pass additional state either down into the recursion or bubble it up in the return values.
1. State Passed Down: Propagating Constraints
Some problems require you to check if a node is valid based on constraints imposed by its ancestors, not just its parent. A classic example is validating a Binary Search Tree (BST).
Validate Binary Search Tree - LeetCode 98 - JavaScript
This video on validating a BST masterfully explains why simply checking node.val > node.left.val is not enough. It demonstrates the need to pass down boundary constraints.
Watch from the point where the presenter explains the flaw in a simple parent-child check, and introduces the idea of min and max bounds. Pay close attention to the conceptual explanation of passing bounds, and then see how it's translated into code in the recursive implementation.
In this BST validation problem, the state for each recursive call is (node, min_bound, max_bound). As you traverse left, you update the max_bound. As you traverse right, you update the min_bound. This preserved state ensures that every node respects the constraints from all its ancestors.
2. State Bubbled Up: Aggregating Results
Other problems involve computing a property of a node based on the results from its children. The state is the information returned from the recursive calls.
"House Robber III" (LeetCode 337) is a prime example of this. The problem is to find the maximum amount of money you can rob from a binary tree of houses, without robbing two directly-connected houses.
To solve this, a DFS approach works best. For each node, you need to know two things from its children:
- The maximum money if you do rob the child.
- The maximum money if you don't rob the child.
The state you return from each recursive call is a tuple: [rob_this_node, skip_this_node].
rob_this_node = node.val + skip_left_child + skip_right_childskip_this_node = max(rob_left_child, skip_left_child) + max(rob_right_child, skip_right_child)
By bubbling up this two-part state, the root can make its final decision. You can find a detailed breakdown of this pattern in the "DSA Prep Guide" under the "House Robber III" problem.
A Framework for Unseen Problems
Now, let's practice applying this "state-centric" thinking to solve unseen Medium problems. We'll use the excellent DSA Prep Guide resource, which provides structured breakdowns.
For each problem, we will ask:
- What is the goal? (e.g., construct, find, merge, validate).
- What information do I need at each step?
- How can I define this as a "state" to pass or maintain?
DSA Prep Guide (Medium-LeetCode, JavaScript Solutions)
This guide provides concise, interview-focused solutions. We will analyze two classic tree problems to see how defining the right state is crucial for an optimal solution.
First, read the entry for Construct Binary Tree from Preorder and Inorder (LC 105). Focus on the comparison between the O(n²) slicing approach and the O(n) index-based approach. The key is understanding why the index-based solution is faster. It defines the state of a subtree not with new array copies, but with (left, right) pointers into the original inorder array. This is a perfect example of choosing the right state representation. Next, read the entry for Lowest Common Ancestor of a Binary Tree (LC 236). Pay close attention to the optimized recursive solution. The state being "bubbled up" is the return value. Analyze what left and right represent. The function returns a node if it's p, q, or the found LCA, and null otherwise. The state being passed up is effectively: "I found one of the nodes," "I found the LCA," or "I found nothing." The logic if (left && right) return root; is where this state is brilliantly used to identify the LCA.
Synthesis: The State-Definition Checklist
When you encounter a new linked-list, stack, or tree problem, use this mental checklist:
- Identify the Core Task: Are you traversing, searching, modifying, or building?
- Consider a Single Step: At an arbitrary node
n, what information is needed to proceed?- Just
nitself? (Simple traversal) - Information about where you came from? (e.g.,
previouspointer for reversal) - Information about other parts of the structure? (e.g.,
fastpointer for cycle detection) - Information from ancestors? (e.g.,
min/maxbounds for BST validation) - Results from descendants? (e.g., child computations for tree DP or LCA)
- Just
- Define the State: Formalize this information into a set of variables. This is your state.
- Examples:
(current, prev),(slow, fast),(node, min, max),(inorder_start, inorder_end).
- Examples:
- Determine State Flow:
- Is the state updated iteratively in a loop? (e.g., linked list reversal)
- Is it passed down as arguments in a recursive call? (e.g., BST validation)
- Is it passed up as a return value from a recursive call? (e.g., LCA)
This process transforms a vague problem into a concrete plan of action, helping to mitigate the "algo phobia" you mentioned. It provides a structured way to think, rather than trying to guess a pattern.
Key Takeaways
- Solving Medium-level problems on linked structures is about defining and managing state during traversal or pointer manipulation.
- "State" is the essential information needed at each step, going beyond just a
currentpointer. - Common State Patterns:
- Linked Lists:
(previous, current, next)for modifications;(slow, fast)for cycle/structural properties. - Trees (Recursive DFS):
- State can be passed down via arguments to enforce constraints from ancestors (e.g.,
(min, max)bounds in BST validation).
- State can be bubbled up via return values to aggregate results from subtrees (e.g., returning found nodes for LCA, or
[rob, notRob]for tree DP).
- State can be passed down via arguments to enforce constraints from ancestors (e.g.,
- Linked Lists:
- By consciously identifying the required state, you can systematically design an algorithm instead of relying on pure pattern matching.
In our next lesson, we will apply this same rigorous, pattern-based thinking to graph problems. You'll learn how to model a problem as a graph and then select the appropriate traversal or algorithm—like BFS, DFS, Dijkstra's, or topological sort—based on the specific question being asked.
Can't find a good explanation? Sign up and we'll make it for you
Sign up