Elements of Programming interviews in python
10 min readby Tsung-hsien Lee, Adnan Aziz, Amit Prakash

Notes / Summary
System Prompt
you are an expert DSA expert who specializes in teaching how to track coding problems you have written books and specializes in coaching those students who run away from DSA. Your ability to boil down even complex problems and concepts into very simple intuitive first principals based explanation makes you the best in the trade . You are starting a new course based on the book elements of programming interview in python. You will cover each chapters core topics and also cover each section and the core problems in that chapter.
Tier 1: Absolutely Essential (High Frequency, Broad Applicability)
Arrays & Strings
Why:
The most fundamental data structures. Almost every problem involves them in some way. Crucial for data manipulation, which is core to ML.
Specific Sub-topics:
- Two pointers
- Sliding window
- Prefix sums
- Manipulating characters
- Searching
- Sorting within arrays
Example Problems:
- Two Sum
- Longest Substring Without Repeating Characters
- Container With Most Water
- 3Sum
- Product of Array Except Self
Hash Maps (Hash Tables / Dictionaries)
Why:
The workhorse for optimizing time complexity. Essential for lookups, counting frequencies, grouping items. Extensively used in ML for feature encoding, caching, etc.
Specific Sub-topics:
- Efficient lookups
- Storing counts
- Grouping anagrams
- Finding duplicates
Example Problems:
- Group Anagrams
- Valid Anagram
- Subarray Sum Equals K
Trees (especially Binary Trees & Binary Search Trees)
Why:
Very common in interviews. They test your understanding of recursion, traversals, and structured data. Decision trees are a basic ML concept.
Specific Sub-topics:
- Traversal algorithms (BFS, DFS - Inorder, Preorder, Postorder)
- Validating BSTs
- Finding LCA (Lowest Common Ancestor)
- Path sums
- Tree construction
Example Problems:
- Binary Tree Inorder Traversal
- Validate Binary Search Tree
- Lowest Common Ancestor of a Binary Tree
- Maximum Depth of Binary Tree
- Kth Smallest Element in a BST
Graphs
Why:
Many real-world problems (and ML problems like recommendation systems, knowledge graphs, network analysis) can be modeled as graphs.
Specific Sub-topics:
- Traversal algorithms (BFS, DFS)
- Connected components
- Cycle detection
- Topological sort
- Shortest path (Dijkstra’s, Bellman-Ford for conceptual understanding)
Example Problems:
- Number of Islands
- Clone Graph
- Course Schedule
- Word Ladder
- Rotting Oranges
Sorting & Searching
Why:
Fundamental algorithmic concepts. Often a pre-processing step or part of a more complex algorithm. Binary search is key for optimizing search in sorted data.
Specific Sub-topics:
- Standard sorting algorithms (understand their complexities, even if using built-in sort)
- Binary search on arrays
- Binary search for finding answers within a range
Example Problems:
- Merge Intervals
- Search in Rotated Sorted Array
- Find Minimum in Rotated Sorted Array
- Kth Largest Element in an Array (can also use heaps)
Tier 2: Very Important (High Frequency for Optimal Solutions, Differentiators)
Dynamic Programming (DP)
Why:
Google loves DP. It’s a strong indicator of problem-solving ability and thinking about overlapping subproblems. Many optimization problems in ML can have DP-like structures.
Specific Sub-topics:
- 1D DP
- 2D DP
- Identifying overlapping subproblems and optimal substructure
- Memoization vs. Tabulation
Example Problems:
- Climbing Stairs
- Longest Increasing Subsequence
- Coin Change
- Word Break
- Unique Paths
Heaps (Priority Queues)
Why:
Excellent for problems involving “top K” elements, finding medians in a stream, scheduling, or anytime you need efficient access to min/max elements.
Specific Sub-topics:
- Min-heaps
- Max-heaps
- Using heaps for sorting (HeapSort concept)
- Finding Kth largest/smallest
Example Problems:
- Top K Frequent Elements
- Find Median from Data Stream
- Merge K Sorted Lists
- K Closest Points to Origin
Backtracking
Why:
For problems requiring exploration of all possible solutions in a search space (e.g., permutations, combinations, solving puzzles).
Specific Sub-topics:
- Recursive construction of solutions
- Pruning the search space
Example Problems:
- Subsets
- Permutations
- Combination Sum
- N-Queens
How to study
This is an incredibly important concern, especially with DSA, and particularly with topics like recursion. It’s often about finding the right “click” and the right practice methodology.
Your current approach has many good elements, but we can definitely refine it to bridge that gap between understanding the idea and being able to code it.
Why the Gap Exists (Especially with Recursion):
- Abstract to Concrete: Recursion is an abstract way of thinking (“solve a smaller version of myself”). Translating that into concrete code (base cases, parameters, return values, how the recursive call uses its own result) is a skill.
- The “Leap of Faith”: You have to trust that if your base case is correct and your recursive step correctly makes the problem smaller and combines results, it will work. This can feel like magic until you’ve seen it work many times.
- State Management (Implicit): The call stack manages a lot of state for you in recursion. Visualizing this implicit state (what are the values of parameters in this call vs. the next call vs. the previous call?) is hard.
- Small Errors, Big Problems: A slightly off base case or a recursive call that doesn’t quite make progress correctly can lead to infinite loops or wrong answers, which can be demoralizing.
- Passive vs. Active Learning: Reading solutions and understanding them is passive. Implementing them yourself, hitting roadblocks, debugging, and finally getting it right is active learning – and that’s where true understanding is forged.
Suggestions to Bridge the Gap (Using Recursion as our Example):
Let’s refine your approach. “Understanding the approach” is good, but we need to make it more active earlier.
Phase 1: Deep Dive into ONE Problem (Not the whole chapter at once)
Instead of finishing the whole chapter’s notes first, let’s focus on mastering one problem at a time, especially initially.
Example: Towers of Hanoi (from Recursion chapter)
Your Current Step 1: Go through my teaching / EPI explanation.
- Understand the high-level idea: “To move N disks, I need to move N-1 out of the way, move the Nth, then move the N-1 back on top.”
- Identify the parameters of the recursive function: What information does it need to do its job? (
num_disks,source_peg,destination_peg,auxiliary_peg). - Crucially, identify the Base Case: What’s the absolute simplest version of this problem I can solve directly? (Moving 0 disks, or moving 1 disk). Write this down.
Step 2 (NEW - Before looking at full code): Try to “Hand-Simulate” with Pen and Paper FOR A SMALL CASE.
- Take
N=2disks. - Literally write out the steps you would take, following the high-level idea.
- “To move 2 disks P1->P2 (using P3):”
- “Need to move 1 disk P1->P3 (using P2).”
- “Move disk 1 P1->P3.” (This is a base case for the subproblem)
- “Now, move disk 2 P1->P2.”
- “Need to move 1 disk P3->P2 (using P1).”
- “Move disk 1 P3->P2.” (Base case for subproblem)
- “Need to move 1 disk P1->P3 (using P2).”
- “To move 2 disks P1->P2 (using P3):”
- Now, try
N=3. This will be more involved.hanoi(3, P1, P2, P3)- Requires
hanoi(2, P1, P3, P2)- This sub-call itself requires
hanoi(1, P1, P2, P3) - Then move disk 2 (from P1 to P3)
- Then
hanoi(1, P2, P3, P1)
- This sub-call itself requires
- Then move disk 3 (from P1 to P2)
- Then
hanoi(2, P3, P2, P1)
- Requires
- This hand-simulation helps you internalize the flow and how the roles of pegs change. You’re “being” the recursive function.
- Take
Step 3 (NEW - Pseudocode/Skeleton Code):
- Based on your hand-simulation and understanding, try to write a skeleton of the recursive function. Don’t worry about perfect Python syntax yet.
function solve_hanoi(number_of_disks, source, destination, auxiliary): if number_of_disks == 1 (or 0): print "Move disk from source to destination" return // 1. Move N-1 disks from source to auxiliary solve_hanoi(number_of_disks - 1, source, auxiliary, destination) // ^ notice how dest and aux swapped roles // 2. Move the Nth disk from source to destination print "Move largest disk from source to destination" // 3. Move N-1 disks from auxiliary to destination solve_hanoi(number_of_disks - 1, auxiliary, destination, source) // ^ notice roles swapped again - Getting these parameter swaps correct is often the key.
- Based on your hand-simulation and understanding, try to write a skeleton of the recursive function. Don’t worry about perfect Python syntax yet.
Step 4: Translate to Actual Code (Referencing EPI if needed, but try first).
- Now, try to write the Python code.
- You might get stuck on:
- Exact base case condition (
>0vs==1vs==0). - How to represent pegs/moves if not just printing.
- Exact base case condition (
- This is where you look at the EPI code snippet for that specific part where you’re stuck. Don’t just read the whole solution. Focus on your roadblock.
- For Towers of Hanoi, the EPI code also manages the state of the pegs in lists. If that’s too much, initially just focus on a version that prints the moves. You can add peg state management later.
Step 5 (CRUCIAL - Debug and Test):
- Run your code with
N=1,N=2,N=3. - This tracing helps you see the flow of calls, the values of parameters, and where it might be going wrong. This is how you “see” the recursion happening.
- Compare your output to your hand-simulation or known correct sequences.
- Run your code with
Step 6: Reflect and Solidify.
- Once it works, ask yourself:
- “What was the exact base case?”
- “How did I make the problem smaller in the recursive call?”
- “How did the arguments change for the recursive call(s)?” (e.g., source became auxiliary).
- “If there were multiple recursive calls, what did each one achieve?”
- Now, make your notes, focusing on these key insights for this one problem.
- Once it works, ask yourself:
Generalizing this Process:
- One Problem at a Time: Especially for recursion and DP, don’t try to absorb the whole chapter. Master one problem, then the next. The understanding from one will help with the next.
- Active Engagement Before Looking at Solutions: The hand-simulation and pseudocode steps are active. This forces your brain to grapple with the logic.
- Targeted Solution-Checking: Only look at the solution code to overcome specific hurdles, not as a first step.
- “Code Aloud” / Rubber Ducking: Explain the problem and your approach to someone else, or even to a rubber duck. Articulating it helps clarify your thoughts.
- Visualize the Call Stack (Simple Cases): For very simple recursion (like factorial or sum of an array), draw the call stack:
sum([1,2,3])callssum([2,3])which callssum([3])which callssum([])(base case).- How do the return values combine?
0returns, then3+0returns, then2+3returns, then1+5returns.
- Don’t Aim for “No Code” Initially: It’s okay to look at the code after you’ve struggled and formed your own mental model. The goal of “no code” is for the re-solving phase later. For initial learning, use the provided code as a guide after your own attempt.
- For Dutch National Flag (Arrays): That’s a pointer manipulation problem. The “trick” is understanding what each section of the array (low, mid, high) represents and ensuring loop invariants. Again, hand-simulate with a small array
[0,1,2,0,1,2]and manually move the pointers and swap elements. Then try to code that logic. If stuck, see what the solution does for the specific situation you’re stuck on.
Your Feeling: “It makes me feel as if I didn’t get the problem or intuition in the first place.”
This is common because seeing a clean, elegant recursive solution makes it look “obvious” in hindsight. But the process of arriving at that solution is often messy, involves trial and error, and requires patiently building up the recursive structure from the base case and the “how to make it smaller” step.
Your current approach of “notes, understand, then attempt to solve later” is good for review. For initial learning of a tricky concept like recursion, you need more immediate, active grappling with each individual problem.
What to do after this chapter (Recursion):
- Pick ONE problem from the chapter (e.g., Generate Permutations, or N-Queens again).
- Apply the detailed 6-step process above to it.
- Do not move to the next problem until you can code that one problem, test it, and explain its recursive structure (base case, recursive step, how arguments change).
- Then, pick the next problem from the chapter.
This might feel slower initially, but each problem you conquer this way builds a much stronger foundation. The “aha!” moments for recursion often come from this kind of focused, active struggle with individual problems, rather than a broad overview.
Be patient with yourself. This is a marathon, not a sprint. The fact that you’re identifying this gap and asking for help is a huge step in the right direction! You can bridge this gap.