I've been studying data structures and algorithms in C++ for a while and have solved roughly 200 practice problems, mostly easy and medium ones with a few hard problems mixed in. I also participate in contests, but my performance has plateaued.
The main issue is that I can usually understand a solution once someone explains it, yet I struggle to discover the approach independently. When I see a new problem, I immediately start guessing: is it binary search, greedy, dynamic programming, hashing, or something else? That often seems to distract me from first understanding the problem's structure.
I'm currently studying trees and recursion and am trying to understand what recursive calls actually return, how the left and right calls interact, and how information moves back up the tree. Those mechanics are becoming clearer, but I don't yet know how to turn that understanding into better independent problem solving.
I also tend to become impatient during contests and want to read the implementation too quickly. I have broad exposure to many topics, but not enough depth, and I worry that I'm memorizing patterns rather than learning how to derive techniques from a problem's properties.
What practice process helped you make this transition? How long should you work before taking a hint? When reviewing a problem you couldn't solve, what should you extract so the review improves your general ability rather than just teaching you one solution? Is it better to study fewer topics deeply, and how can I stop asking which algorithm a problem uses and instead ask what features of the problem imply an approach?
4 Answers
There is a difference between studying data structures and algorithms and collecting solutions from coding platforms. The latter can build familiarity with common patterns, but it does not automatically provide the theoretical foundation behind them. Spend time learning what each structure or algorithm is designed to support, including its invariants, trade-offs, and complexity. Implement the core structures and algorithms yourself, write pseudocode, and use small examples to test your understanding.
When reviewing a missed problem, record the problem's constraints, the failed brute-force idea, the key observation, and why the final method satisfies the constraints. That is much more useful than saving the code alone.
Before looking for an advanced technique, write down a straightforward brute-force approach and explain why it is too slow. Identify the size of the search space, where repeated work occurs, and what information would let you avoid that work. Those observations often lead naturally to the data structure or algorithm. For recursion, focus less on visualizing every stack frame and more on defining exactly what each call promises to return to its parent.
Set a firm limit, such as 45 minutes of uninterrupted reasoning with examples and pseudocode. If you still cannot solve it, study the solution actively: identify the observation you missed, close the solution, and recreate the method yourself.
Don't assume there is one specific solution you were supposed to discover. Many problems have several valid approaches, including different ways to express the same optimal idea. Your goal is to produce a correct solution and improve it, not to guess the author's exact pattern.
Try implementing the simplest correct version first. Once it works, inspect its complexity and ask what makes it too slow. That keeps optimization grounded in the problem instead of turning the beginning into a guessing game.
A plateau can come from having broad exposure without enough depth. Pick one topic for a while and study it from several angles: the underlying concept, a plain implementation, simple exercises, and problems where the technique is useful in less obvious ways. Also remember that contest puzzles are specialized riddles; struggling with them does not necessarily mean you cannot reason well as a programmer.
After reading a solution, wait a day or two and solve the problem again from a blank editor. Then solve a small variation without looking at the original. If you can explain why the method works and recognize when its assumptions no longer hold, you learned more than just the template.

That makes sense. I tend to reject a basic approach too early because I feel I should recognize the optimal technique immediately. Starting with a correct version should give me something concrete to improve.