#30DaysofCode September: My journey to DSA Mastery
This September, I embarked on a self-imposed 30 Days of Code challenge, a commitment to solve at least two Data Structures and Algorithms (DSA) problems every single day. My goal was to push myself out of my comfort zone, build consistency, and improve my problem-solving skills and programming logic.
I didn’t just stick to one platform — I completed HackerRank’s 30 Days of Code challenge, LeetCode’s 30 Days of JavaScript plan, and also tackled LeetCode’s Top Interview 150 track, a collection of 150 typical interview problems for anyone prepping for a coding interview. Additionally, I took a few lessons on Structy to further strengthen my grasp of DSA concepts.
I solved most problems using JavaScript (except for a few HackerRank problems where JavaScript wasn’t supported, so I used Python instead). To help keep myself accountable, as well as share my progress, I posted the challenges I solved each day on Twitter. You can find them in this quote trail:
Data Types, Basic to Advanced
Each day exposed me to different types of data structures and algorithms. I learned to handle
- arrays
- strings
- objects
- maps
- sets
- numbers
- booleans, and more.
Some problems involved more complex structures like linked lists and binary trees, while others threw matrices / n-D arrays into the mix, letting me deal in multidimensional spaces.
The diversity of the problems kept me on my toes. The problems on LeetCode’s 30 Days of JavaScript track introduced me to fundamentals like
- array transformations
- function transformations
- closures
- classes
- JSONs
- Promises & time.
LeetCode’s Top Interview 150 track took things a step further, tossing me between problems of varying difficulty and approach. Solving at least one of these each day was invaluable. These problems also challenged me to think more often about optimization and how different approaches could drastically affect runtime and memory efficiency.
I also took Structy lessons alongside these challenges to solidify my understanding of key concepts. I learned more efficient ways to handle some data structures, like the sections on linked lists and binary trees. I came to especially appreciate the platform’s dynamic approach to teaching and breaking down problems in a way that makes them easy to digest. It’s a great platform to learn DSA at a very basic level and then translate this understanding to other coding problems I face.
Approaches and Techniques
As the days progressed, I encountered many kinds of solution approaches, problem-solving techniques such as
- recursion
- two-pointer
- hashmap
- hashtable
- greedy algorithms
- binary search
- sliding window
- dynamic programming
- caching (memoization), and more.
I found the two-pointer approach a straightforward yet powerful way to work through problems involving sequences (primarily arrays and strings), by reducing the search space for more efficient solutions. I also used greedy algorithms where I needed to make locally optimal choices at each step.
Sliding window techniques were another eye-opener— initially challenging, but valuable for problems involving subarrays or substrings; problems that required keeping track of a moving range of elements within a sequence. Hashmaps and hashtables are great for key-value pairs in problems involving frequent lookups.
One of the most complex techniques I worked with was dynamic programming. Early on, I found DP quite intimidating due to the difficulty in recognizing overlapping subproblems, but with repeated exposure memoization became my best friend in optimizing recursive calls.
Function Manipulation (feat. Wrappers & Prototypes)
In addition to core DSA problems, I also got the chance to try out more advanced JavaScript concepts. I wrote wrapper functions, learning how to control the flow of my code and extend functionality for specific requirements. I even learned to set timers on functions which allows me handle time-sensitive operations or limit how often certain functions can execute.
Working with prototypes showed me that data types can be manipulated beyond their inbuilt capabilities by creating custom methods. This allowed me to extend the inbuilt functionality of data types like objects and arrays in new ways. I came to appreciate the importance of writing clean, modular code.
I also learned memoization, a technique where you modify a function such that it stores previously called arguments with their results! This is a kind of caching that helps improve performance in problems where there are multiple calls to the same function. I wrote about my Memoize solution that beat 99% (memory) here: Memoize Intutitive Solution beats 99% Memory.
Testing & Optimization
Another thing I started to pay more attention to during this journey was the optimization of my code. After a proper introduction to big-O notation on Structy, I understood the importance of time and space complexity and I learned the optimal technique for most common problems. Toward the end of the HackerRank challenge track, I also learned to test code using classes and to write generics.
Takeaway
By the time I completed the 30 Days of Code challenge, I felt a profound sense of growth. It improved my ability to handle a wide range of problems and deepened my understanding of JavaScript and DSA. More than just solving problems, this month-long journey was a reminder to myself that I really could put in the effort. Seeing the rewards of consistency and discipline, I have been renewed with the mindset to approach any challenge with confidence and a strong toolkit of techniques.
This experience marks a significant milestone in my programming journey. The skills I’ve sharpened over these 30 days will undoubtedly carry forward into my career as a software engineer.
I am proud of me for having put in the effort, and grateful to my friends who cheered me on and offered their support and wisdom throughout the challenge.