Why You’re Practicing Daily But Still Can’t Solve Medium LeetCode Problems

You’ve been grinding LeetCode problems every day for weeks or even months. You’ve watched countless YouTube tutorials, read all the recommended books, and even joined study groups. Yet when you face those medium-level problems, you still freeze up. Sound familiar?
If you’re nodding along, you’re not alone. Many aspiring developers find themselves stuck in this frustrating plateau despite putting in consistent effort. In this comprehensive guide, we’ll explore why you might be struggling and, more importantly, how to break through this barrier.
The Common Struggle With Medium LeetCode Problems
Let’s start by acknowledging a harsh truth: solving algorithm problems is hard. It’s not supposed to be easy, especially at the medium and hard levels. But the difference between those who eventually break through and those who remain stuck often isn’t raw intelligence or coding experience—it’s their approach to learning and practice.
Here’s what typically happens:
- You solve some easy problems and gain confidence
- You attempt medium problems and get stuck
- You look at the solution, understand it, and think “I could have done that”
- You move to the next problem, only to repeat the same cycle
- After weeks of this, you feel like you’re not making progress
This cycle is demoralizing, but understanding why it happens is the first step to overcoming it.
7 Reasons You’re Still Struggling With Medium Problems
1. You’re Not Building The Right Mental Models
One of the biggest reasons programmers struggle with medium LeetCode problems is that they’re trying to memorize solutions rather than understanding the underlying patterns and building mental models.
Think of it this way: if you only memorize the solution to “Two Sum,” you’ve learned to solve exactly one problem. But if you understand the pattern of using a hash map to track values you’ve seen before, you’ve gained a tool that applies to dozens of problems.
Mental models are frameworks that help you approach and break down problems. They’re like templates your brain can quickly adapt to new situations. Without these models, each new problem feels like starting from scratch.
2. You’re Rushing Through Problems
In the age of “grinding LeetCode,” there’s pressure to solve as many problems as possible. This quantity-over-quality approach often leads to superficial understanding.
If you look at the solution after just 10-15 minutes of struggle, you’re robbing yourself of the most valuable part of the learning process: the struggle itself. The mental connections formed during that struggle are what build lasting problem-solving abilities.
Consider this research-backed learning principle: we learn better when we retrieve information from memory rather than simply reviewing it. This is called the “retrieval practice” effect. By giving up too quickly, you’re missing out on this powerful learning mechanism.
3. You’re Not Reviewing Problems Effectively
Many programmers solve a problem, feel good about it, and never look back. But one-time exposure is rarely enough to cement understanding, especially for complex concepts.
Effective learning requires spaced repetition—returning to problems after increasing intervals of time. Without this systematic review, you’re likely forgetting solutions almost as quickly as you’re learning them.
Research shows that we forget approximately 70% of what we learn within 24 hours without proper review. By day 30, without any reinforcement, we retain only about 2-3% of the original information.
4. You Haven’t Mastered The Fundamentals
Medium problems typically combine multiple fundamental concepts. If your understanding of these basics is shaky, you’ll struggle when they’re combined in new and complex ways.
For example, a medium-level dynamic programming problem might require solid understanding of:
- Recursion
- Array manipulation
- Basic DP concepts like memoization
- Problem-specific insights
If any of these foundations is weak, the entire solution becomes difficult to construct.
5. You’re Not Actively Coding Solutions
Reading solutions and watching tutorials creates an illusion of understanding. You follow along, nodding in agreement, thinking “this makes sense.” But passive consumption is very different from active creation.
Many learners fall into the trap of reading multiple solutions or watching several tutorials without actually implementing the code themselves. This passive learning gives a false sense of mastery that quickly evaporates when facing a blank editor.
6. You Haven’t Developed A Systematic Approach
Successful problem solvers don’t just dive into coding. They have a systematic approach:
- Understanding the problem completely
- Working through examples manually
- Looking for patterns and edge cases
- Considering multiple approaches before coding
- Testing their solution with various inputs
Without this structured method, you might find yourself jumping between partial solutions or getting stuck in unproductive thought loops.
7. You’re Dealing With Psychological Barriers
Finally, don’t underestimate the psychological aspects of problem-solving. Many programmers develop mental blocks around certain problem types or experience “solution paralysis” when faced with a blank editor.
Imposter syndrome, anxiety about time constraints, and fear of failure can all interfere with your cognitive processes. These psychological barriers can be just as limiting as knowledge gaps.
How To Break Through The Medium Problem Barrier
Now that we understand the common roadblocks, let’s explore practical strategies to overcome them and level up your problem-solving skills.
Focus On Patterns, Not Problems
Instead of treating each problem as a unique challenge, start grouping them into pattern categories. Here’s a framework to get started:
Core Patterns To Master:
- Two Pointers/Sliding Window: For array/string problems that involve subarrays or require comparing elements
- Hash Map Lookups: For efficient value retrieval and frequency counting
- Breadth-First Search (BFS): For level-by-level exploration of trees/graphs and finding shortest paths
- Depth-First Search (DFS): For exploring all possible paths and backtracking scenarios
- Binary Search: For problems involving sorted arrays or solution spaces
- Dynamic Programming: For optimization problems with overlapping subproblems
- Graph Traversal: For network-related problems and relationship modeling
When you encounter a new problem, try to identify which pattern it matches before diving into the solution. With practice, this pattern recognition becomes faster and more intuitive.
For example, consider these medium problems and their patterns:
Problem | Pattern |
---|---|
Longest Substring Without Repeating Characters | Sliding Window + Hash Map |
3Sum | Two Pointers |
Number of Islands | DFS/BFS on Grid |
Coin Change | Dynamic Programming |
Implement The 20-50-30 Rule
To structure your practice more effectively, follow the 20-50-30 rule:
- 20% of your time: Spend on understanding the problem and planning your approach
- 50% of your time: Struggle with implementing the solution yourself
- 30% of your time: Review, refine, and learn from solutions
Here’s how to apply this in practice:
The 20% Phase: Problem Understanding
- Read the problem statement multiple times
- Work through the examples manually to understand the expected output
- Identify edge cases (empty arrays, negative numbers, etc.)
- Determine what pattern category this problem might fall into
- Sketch a high-level approach before writing any code
The 50% Phase: Productive Struggle
- Attempt to implement your solution without looking at hints
- If you get stuck, take a short break and return with fresh eyes
- Try to solve simpler versions of the problem first
- Use debugging tools to understand where your solution fails
- Only move to hints if you’ve been truly stuck for 30+ minutes
The 30% Phase: Review and Internalize
- Compare your solution to the official solution
- Understand any optimizations you missed
- Reimplement the solution from memory the next day
- Document the key insights in your own words
- Add the problem to your spaced repetition system for future review
Build A Deliberate Review System
Spaced repetition is crucial for long-term retention. Here’s how to implement it for algorithm problems:
Create A Review Schedule:
- Day 1: Solve the problem
- Day 2: Reimplement from memory
- Day 4: Quick review of approach
- Day 7: Reimplement from memory
- Day 14: Final review
- Monthly: Revisit key problems
You can use tools like Anki or simply maintain a spreadsheet with problem details and review dates. The key is consistency and actually implementing the solutions again rather than just reading them.
For Each Review, Ask Yourself:
- What pattern does this problem follow?
- What was the key insight that made the solution work?
- What edge cases need special handling?
- How could I optimize this solution further?
Master One Pattern At A Time
Rather than jumping between different problem types, focus on mastering one pattern category at a time. This approach builds deeper understanding and confidence.
For Example, A Sliding Window Deep Dive:
- Start with basic fixed-length window problems
- Move to variable-length windows with simple conditions
- Progress to more complex window conditions
- Tackle problems combining sliding window with other techniques
Spend at least a week on each pattern category, solving 5-10 problems of increasing difficulty. Only move on when you can comfortably solve new problems using that pattern without hints.
Verbalize Your Thought Process
One powerful technique used by expert problem solvers is thinking aloud. This forces you to clarify your reasoning and often reveals gaps in your understanding.
Try these approaches:
- Explain the problem and your solution approach to an imaginary colleague
- Record yourself solving a problem and narrating your thought process
- Join a study group where you take turns explaining solutions
- Write detailed comments in your code explaining each step
This practice not only improves your problem-solving but also prepares you for technical interviews where communicating your approach is crucial.
Implement The “Solve It Three Ways” Challenge
For medium problems you’ve solved, challenge yourself to implement three different solutions:
- A brute force approach (to ensure you understand the problem basics)
- An optimized solution using the appropriate pattern
- A different approach using an alternative technique
This exercise forces you to think beyond your first solution and deepens your understanding of the problem space.
For example, for the “Maximum Subarray” problem:
- Approach 1: Brute force (check all possible subarrays)
- Approach 2: Kadane’s algorithm (dynamic programming)
- Approach 3: Divide and conquer
Use Code Templates Strategically
Having code templates for common patterns can accelerate your learning and reduce cognitive load when solving new problems. Here’s a simple template for the sliding window pattern:
function slidingWindowTemplate(s) {
let windowStart = 0;
let result = 0;
let windowData = {}; // or other data structure to track window state
for (let windowEnd = 0; windowEnd < s.length; windowEnd++) {
// Add the current element to window data
let rightChar = s[windowEnd];
// Update window data
// Condition to shrink window
while (/* condition to shrink window */) {
// Remove the leftmost element from window data
let leftChar = s[windowStart];
// Update window data
windowStart++;
}
// Update result based on current window
result = Math.max(result, windowEnd - windowStart + 1);
}
return result;
}
The key is not to copy-paste these templates blindly but to understand the underlying structure and adapt it to each specific problem.
Tackling Specific Problem Types That Give You Trouble
Let’s look at strategies for some of the most challenging problem categories that trip up intermediate programmers.
Dynamic Programming Problems
Dynamic programming (DP) problems are notoriously difficult for many programmers. Here’s a systematic approach:
1. Identify The Subproblems
Every DP problem can be broken down into overlapping subproblems. Ask yourself:
- What decision do I need to make at each step?
- What information do I need to make that decision?
2. Define The State Variables
State variables represent the information you need to track. For example:
- In a knapsack problem: current item and remaining capacity
- In a string matching problem: positions in both strings
3. Formulate The Recurrence Relation
This is the heart of DP—how the solution to the current problem relates to solutions of subproblems. Usually takes the form:
dp[i][j] = some_operation(dp[i-1][j], dp[i][j-1], etc.)
4. Identify The Base Cases
These are the simplest scenarios where the answer is directly known without calculation:
- Empty string/array
- First element only
- Zero capacity/value
5. Decide Implementation Approach
- Top-down (memoization): Start with the original problem and break it down recursively, caching results
- Bottom-up (tabulation): Start with base cases and build up to the final solution iteratively
Sample DP Problem Walkthrough: Coin Change
Let’s apply this framework to the classic Coin Change problem: Given coins of different denominations and a total amount, find the fewest coins needed to make that amount.
- Subproblems: For each amount from 0 to target, find minimum coins needed
- State variable: Current amount (amount)
- Recurrence relation: dp[amount] = 1 + min(dp[amount – coin]) for each coin
- Base case: dp[0] = 0 (zero coins needed for amount zero)
- Implementation: Bottom-up approach
function coinChange(coins, amount) {
// Initialize dp array with amount+1 (which is greater than max possible coins)
const dp = Array(amount + 1).fill(amount + 1);
// Base case: 0 coins needed for amount 0
dp[0] = 0;
// Build up the solution
for (let currentAmount = 1; currentAmount <= amount; currentAmount++) {
for (const coin of coins) {
if (coin <= currentAmount) {
dp[currentAmount] = Math.min(dp[currentAmount], 1 + dp[currentAmount - coin]);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
Graph Problems
Graph problems often intimidate programmers, but they become manageable with a structured approach:
1. Understand The Graph Representation
Decide how to represent the graph:
- Adjacency List: Space-efficient for sparse graphs
- Adjacency Matrix: Faster edge lookups for dense graphs
2. Choose The Right Traversal Strategy
- BFS: Best for shortest path and level-by-level exploration
- DFS: Best for exploring all paths and cycle detection
3. Track Visited Nodes
Always keep track of visited nodes to avoid cycles and redundant work.
4. Consider Special Graph Algorithms
For specific problems, specialized algorithms may be needed:
- Dijkstra’s algorithm for shortest paths with weighted edges
- Union-Find for connectivity problems
- Topological sort for dependency ordering
Sample Graph Problem Approach: Number of Islands
Let’s see how to approach the Number of Islands problem: Given a 2D grid where ‘1’ represents land and ‘0’ represents water, count the number of islands.
function numIslands(grid) {
if (!grid || grid.length === 0) return 0;
const rows = grid.length;
const cols = grid[0].length;
let islandCount = 0;
function dfs(r, c) {
// Check boundaries and if it's land
if (r < 0 || c < 0 || r >= rows || c >= cols || grid[r][c] === '0') {
return;
}
// Mark as visited by changing to '0'
grid[r][c] = '0';
// Explore all 4 directions
dfs(r + 1, c);
dfs(r - 1, c);
dfs(r, c + 1);
dfs(r, c - 1);
}
// Traverse the grid
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (grid[r][c] === '1') {
islandCount++;
dfs(r, c); // Sink the entire island
}
}
}
return islandCount;
}
Psychological Strategies For Overcoming Mental Blocks
The mental aspect of problem-solving is just as important as the technical knowledge. Here are strategies to overcome psychological barriers:
Embrace The Struggle
Reframe how you view difficulty. Struggling with problems isn’t a sign of inadequacy—it’s an essential part of the learning process. Research in cognitive science calls this “desirable difficulty”—challenges that enhance long-term learning.
When you encounter a difficult problem, try saying: “This is difficult, and that’s good. My brain is forming new connections right now.”
Use Time-Boxing
Set a fixed time limit for your initial attempt at a problem (e.g., 30-45 minutes). This prevents endless frustration while ensuring you give yourself enough time to struggle productively.
After the time box:
- If you’ve made progress, extend by another time box
- If you’re completely stuck, look at a hint or partial solution
- Never look at the full solution immediately
Practice Metacognition
Metacognition—thinking about your thinking—can help identify patterns in how you approach problems:
- Do you rush to code before fully understanding the problem?
- Do you get fixated on your first approach even when it’s not working?
- Do you skip considering edge cases?
Keep a “problem journal” where you not only document solutions but also reflect on your problem-solving process and emotional responses.
Build A Growth Mindset Community
The people you surround yourself with significantly impact your mindset. Find a community that:
- Celebrates learning over performance
- Normalizes struggle as part of growth
- Provides constructive feedback
- Shares learning resources and approaches
This could be an online forum, a local meetup, or a study group with colleagues.
Creating A Structured Learning Path
To make consistent progress, you need a structured approach rather than randomly picking problems. Here’s a 12-week plan to systematically improve your medium-level problem-solving skills:
Week 1-2: Foundation Strengthening
- Review core data structures (arrays, linked lists, trees, graphs)
- Practice 10 easy problems focusing on clean implementation
- Develop your systematic problem-solving template
Week 3-4: Array and String Patterns
- Focus on two-pointer technique and sliding window
- Solve 8-10 medium problems in this category
- Create templates for these patterns
Week 5-6: Tree and Graph Traversal
- Master BFS and DFS implementations
- Practice binary tree and graph medium problems
- Implement specialized algorithms (Dijkstra’s, topological sort)
Week 7-8: Dynamic Programming Foundations
- Start with 1D dynamic programming problems
- Progress to 2D problems
- Practice both top-down and bottom-up implementations
Week 9-10: Advanced Patterns
- Backtracking problems
- Divide and conquer
- Greedy algorithms
Week 11-12: Integration and Mock Interviews
- Practice problems that combine multiple patterns
- Conduct timed mock interviews
- Review and strengthen weak areas
For each week, follow this daily structure:
- Day 1-3: Learn pattern and solve introductory problems
- Day 4-5: Tackle medium problems with that pattern
- Day 6: Review and implement solutions from memory
- Day 7: Reflect on learnings and plan for next week
Leveraging AI Tools Effectively
Modern AI tools can be powerful allies in your learning journey if used correctly. Here’s how to leverage them without becoming dependent:
For Concept Understanding
Use AI to explain concepts in multiple ways. For example, if you’re struggling with dynamic programming:
- Ask for explanations at different levels of detail
- Request analogies that relate to concepts you already understand
- Have it explain the same concept from different angles
For Solution Review
After implementing your own solution:
- Ask AI to review your code and suggest optimizations
- Have it explain the time and space complexity
- Request alternative approaches to the same problem
For Targeted Hints
When stuck, ask for progressive hints rather than full solutions:
- “What pattern would be useful for this problem?”
- “What’s a key insight I might be missing?”
- “What’s a similar problem I should review first?”
For Custom Problem Generation
Use AI to create variations of problems you’ve solved:
- “Create a variation of this problem with an additional constraint”
- “Generate a similar problem that uses the same pattern but in a different context”
Conclusion: The Path Forward
Breaking through the medium problem barrier isn’t about raw intelligence or memorizing more solutions—it’s about developing a structured approach to learning and problem-solving. By focusing on patterns rather than individual problems, building effective mental models, and implementing consistent review systems, you can systematically improve your algorithm skills.
Remember that everyone—even the most accomplished engineers—struggled with these concepts at some point. The difference is in how they approached that struggle and used it as a catalyst for growth rather than a source of discouragement.
Your daily practice isn’t wasted effort—it’s building neural pathways that will eventually connect into powerful problem-solving capabilities. By applying the strategies in this guide, you can transform that daily practice into meaningful progress.
The journey from struggling with medium problems to solving them confidently isn’t a straight line—it’s a series of plateaus, breakthroughs, and occasional setbacks. Embrace this journey with patience and persistence, and you’ll not only conquer LeetCode mediums but develop problem-solving skills that will serve you throughout your programming career.
What specific pattern or problem type will you focus on mastering first? Start there, apply these principles consistently, and watch as previously challenging problems begin to yield to your improved approach.