Programming

How to Start Learning Data Structures and Algorithms

A roadmap in the order that works, the first four ideas running in real code, and a weekly practice routine that builds recognition rather than memorised solutions.

Modern Age Coders Team
Modern Age Coders Team September 28, 2026
8 min read
Data structures and algorithms roadmap: eight stages from language fluency to dynamic programming

Data structures and algorithms, usually shortened to DSA, is the part of programming that decides whether you can solve problems you have not seen before. It is what technical interviews test, what competitive programming is built on, and what separates someone who can follow a tutorial from someone who can design a solution. It also has a reputation for being overwhelming, mostly because beginners start in the wrong place or try to learn everything at once.

This is a practical roadmap: the order to learn things in, why that order works, the first four structures shown working in real code with real output, and a weekly practice routine that builds the thing interviews actually measure, which is recognising which idea a problem needs.

Before you start: one language, properly

DSA is not a language, and it is not the place to learn one. Before starting, you should be able to write loops, functions, lists and dictionaries in one language without looking things up. Python, Java and C++ are all good choices. Python lets you focus on the ideas with less code. Java and C++ are common in interviews and are faster, which matters in competitive programming. If you are still getting comfortable, our guide on how long it takes to learn Python gives realistic milestones, and Python vs Java helps with the choice.

The roadmap, in order

  1. Arrays, strings and Big O. Learn to describe how fast code is before learning faster code. Our Big O notation guide covers it with measured examples.
  2. Hash maps and sets. The single most useful idea in interview problems. Many slow solutions become fast by remembering what you have seen.
  3. Stacks and queues. Structures where the order of adding and removing is the whole point.
  4. Sorting, binary search and recursion. Splitting and halving problems. Understand recursion properly here, because trees and graphs need it.
  5. Linked lists and trees. Data that points to other data.
  6. Graphs. Breadth-first and depth-first search, for maps, networks and mazes.
  7. Dynamic programming. Remembering answers to sub-problems. Leave it until last: it builds on recursion and hash maps.
๐Ÿ’ก

Do not start with dynamic programming

It is the topic people fear most, so many beginners rush at it. It depends on recursion and hash maps being second nature. Arrive there last and it is far less mysterious.

The first four ideas, working

Here are four of the ideas from the roadmap in short, real programs. Each output is exactly what the code printed.

A stack: checking brackets

A stack is last in, first out, like a pile of plates. It is perfect for anything that must be closed in reverse order of opening, which is exactly how brackets work. Code editors do a version of this to highlight a missing bracket.

brackets.py
def balanced(text):
    pairs = {")": "(", "]": "[", "}": "{"}
    stack = []
    for ch in text:
        if ch in "([{":
            stack.append(ch)                 # remember every opener
        elif ch in pairs:
            if not stack or stack.pop() != pairs[ch]:
                return False                 # closer with no matching opener
    return not stack                         # anything left open is unbalanced

for s in ["(a[b]{c})", "(a[b)]", "((x)", "print(f(x[0]))"]:
    print(f"{s:<16} {balanced(s)}")
Output
(a[b]{c})        True
(a[b)]           False
((x)             False
print(f(x[0]))   True

A hash map: the two-sum problem

Given a list of prices, find two that add up to a target. Checking every pair is slow. Remembering each price in a dictionary as you go makes it one pass, because for each price you only need to ask whether its partner has already been seen.

two_sum.py
def two_sum(numbers, target):
    seen = {}                                # value -> index where we saw it
    for i, n in enumerate(numbers):
        if target - n in seen:
            return seen[target - n], i
        seen[n] = i
    return None

prices = [12, 7, 30, 18, 5, 21]
print("two prices adding to 23 are at positions", two_sum(prices, 23))
print("two prices adding to 100:", two_sum(prices, 100))
Output
two prices adding to 23 are at positions (3, 4)
two prices adding to 100: None

A queue: the shortest way out of a maze

A queue is first in, first out, like a line at a counter. Used for breadth-first search, it explores everything one step away, then two steps, then three, so the first time it reaches the exit it has found the shortest route.

Swap the queue for a priority queue that always hands back the closest place, and breadth-first search becomes Dijkstra's algorithm, the basis of route finding. Our guide to how maps find the fastest route builds it step by step.

maze.py
from collections import deque

maze = ["S.#.....",
        ".##.###.",
        "....#...",
        "#.#...#E"]

def shortest_path(maze):
    rows, cols = len(maze), len(maze[0])
    start = next((r, c) for r in range(rows) for c in range(cols) if maze[r][c] == "S")
    queue = deque([(start, 0)])
    seen = {start}
    while queue:
        (r, c), steps = queue.popleft()      # a queue explores nearest squares first
        if maze[r][c] == "E":
            return steps
        for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
            nr, nc = r + dr, c + dc
            if 0 <= nr < rows and 0 <= nc < cols and maze[nr][nc] != "#" and (nr, nc) not in seen:
                seen.add((nr, nc))
                queue.append(((nr, nc), steps + 1))
    return None

print("shortest path from S to E:", shortest_path(maze), "steps")
Output
shortest path from S to E: 12 steps
A small maze solved by breadth-first search, with the shortest path from S to E being 12 steps
Nearest squares first, so the first arrival is the shortest path.

If data is sorted, you never need to check it all. Look at the middle, throw away the half that cannot contain the target, repeat.

binary_search.py
def binary_search(sorted_items, target):
    low, high, checks = 0, len(sorted_items) - 1, 0
    while low <= high:
        checks += 1
        mid = (low + high) // 2
        if sorted_items[mid] == target:
            return mid, checks
        if sorted_items[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return None, checks

roll_numbers = list(range(1000, 101000, 3))     # 33,334 sorted roll numbers
print(len(roll_numbers), "items")
print("find 70003:", binary_search(roll_numbers, 70003))
print("find 70004 (not there):", binary_search(roll_numbers, 70004))
Output
33334 items
find 70003: (23001, 13)
find 70004 (not there): (None, 15)

Over thirty thousand items, and binary search needed 13 checks to find the value and only 15 to be certain a missing one was not there. Checking one by one could take over thirty thousand. That halving is where every O(log n) comes from.

Matching the structure to the job

Which data structure to use for common jobs: hash maps for lookup, stacks for undo and brackets, queues for nearest-first, binary search for sorted data, trees for hierarchies and graphs for connections
Most problems announce the structure they need, if you know what to listen for.
If the problem involves... Reach for
Looking things up by name or id, counting, "have I seen this?" A hash map or set
Undo, matching pairs, going back the way you came A stack
Processing in arrival order, nearest first, levels A queue
Finding a value in sorted data, or "the smallest x that works" Binary search
Hierarchies: folders, menus, family trees A tree
Connections: roads, friendships, dependencies A graph with BFS or DFS

How to practise so it actually sticks

A weekly DSA practice routine: learn one idea, solve easy problems on it, solve mixed problems, and re-solve last week's problems from memory
Mixed problems and re-solving are where recognition is built.
  • Trace before you type. For every new idea, work one example by hand on paper, following each step.
  • Do labelled problems first, then mixed. Problems grouped under a heading like "stacks" teach the technique. Mixed problems teach the harder skill of recognising which technique is needed.
  • Re-solve after a week, from a blank file. If you cannot, you understood the solution without owning it.
  • Explain your solution out loud. Interviews test this directly, and explaining exposes gaps quickly.
  • Two or three good problems a day beat ten rushed ones. Depth builds recognition. Volume without reflection mostly builds anxiety.
โš ๏ธ

The tutorial trap

Watching someone solve a problem feels like progress. It is not, until you can solve a similar one yourself. After watching or reading any solution, close it and rewrite it from scratch.

How long does it take?

As a planning estimate from teaching, not a measured average: a student who already codes comfortably and practises about five to seven hours a week usually covers stages 1 to 4 in two to three months, and reaches a solid interview level, stages 1 to 7, in six to nine months. Competitive programming at a high level takes longer, and our post on moving from USACO Bronze to Silver shows the kind of wall students meet there.

Learning DSA is not memorising solutions. It is learning to hear which idea a problem is asking for.

How we teach it

Our data structures and algorithms course follows the principles on our how we teach page: one concept fully understood before the next, and code traced line by line until the student can predict every step. It runs live in Python, Java or C++, one to one or in small groups of 5 to 10, and there are dedicated Java DSA and C++ DSA tracks.

Frequently asked questions

First become fluent in one programming language. Then learn Big O, arrays and strings, hash maps, stacks and queues, sorting and binary search, recursion, trees, graphs and finally dynamic programming, in that order. Practise each with a few problems before moving on.

Python, Java and C++ are all good. Python needs the least code, so ideas are clearer. Java and C++ are common in interviews and faster, which matters in competitive programming. Use the language you already know best to start.

As a rough estimate, someone who already codes comfortably and practises five to seven hours a week can cover the core topics in two to three months and reach a solid interview level in six to nine months.

Basic maths is enough to start: arithmetic, logarithms at an intuitive level, and simple counting. Some advanced topics use more maths, but most interview-level DSA relies far more on logical thinking than on formulas.

Arrays. They are simpler, more commonly used, and the basis for understanding everything else. Linked lists make more sense once you know why arrays can be slow for inserting in the middle.

Quality matters more than quantity. Two or three problems a day, with time spent understanding and re-solving them, beats rushing through many. A few hundred well-understood problems across all topics is enough for most interviews.

No. It helps you write faster, clearer code in any project, and it is the foundation of competitive programming and computer science courses. Interviews test it because it reflects general problem-solving ability.

Modern Age Coders Team

About Modern Age Coders Team

Expert educators making coding and maths clear for ages 6 to 67.

Keep exploring Modern Age Coders

More from the blog

Learn more

Free resources

From the blog

Start here

Ask Misti AI
Chat with us
Enroll Watch Class Priority Demo Enrol Book a Demo Watch Class WhatsApp Book demo today