Express course · No. 23
A data structure is just a way of organising data so that the things you do most often are fast. Pick the right one and an operation is instant; pick the wrong one and the same task crawls. Big-O is the language for that difference — how the cost of an operation grows as the data grows.
16 concepts · a diagram for each · cheat sheet at the end
Before the specific structures, the idea behind all of them. A data structure isn't academic — it's the single choice that most often decides whether your code is fast or painfully slow.
The map: 16 concepts in six groups
A metro map: first you see all the lines, then you ride station by station.
On top sit three basic ideas: Data Structure, Trade-off and Big-O. Below them, six groups: the Big-O growth shapes, the array, the hash map and set, structures with an order (stack and queue), structures with a shape (tree and graph), and finally how to choose among them in practice.
How to read this course
A passport: the same fields for everyone, so they're easy to compare.
Every concept gets the same treatment: its English name (the term you'll meet in docs and interviews), a picture from everyday life, a diagram and a card: what it is, how it works, an example, the cost, and how it relates to other structures. The one rule of this course: there is no best structure — only the one that fits the operation you do most.
Data Structure — organising data for the operations you do most
A tidy toolbox with labelled slots versus a heap of tools in a sack — the same tools, but one lets you grab the right one instantly and the other means digging every time.
A data structure is a way of arranging data in memory. Different arrangements make different operations fast: one is great at looking things up by name, another at keeping order, another at always grabbing the most recent item. The whole game is matching the structure to what you'll do most.
The same task can be trivial or nearly impossible depending on how the data is held. Finding a word in a sorted dictionary takes seconds; finding it in the same words dumped in random order could take all day. So a surprising amount of "make this fast" is really "store it in the right shape." The structure is the decision.
- What it is
- A way of arranging data in memory for particular operations.
- How it works
The arrangement decides up front which operations are cheap and which are expensive.
- Example
Looking up a user by ID: instant in one structure, a slog through everything in another.
- Cost
- A wrong choice isn't fixed by "clever optimisation" later.
- Related
Structures are compared with Big-O and chosen through a Trade-off.
Trade-off — every structure gives something up
A sports car and a moving van are both vehicles — you don't ask which is better, you ask better for what.
No structure is best at everything. One gives instant lookup but no order; another keeps order but is slow to search; another is fast to add to but slow to find within. That's the trade-off: strength in one operation is paid for with weakness in another. There's no free lunch — your job is picking the trade that fits your use.
- What it is
A structure's strength in one operation always costs weakness in another.
- Example
A Hash Map finds by key instantly but keeps no order; an Array keeps order but is slow to search by value.
- When to think
Every time you decide where data lives: what do I do often, and what rarely?
- Related
- The trade is measured in Big-O — every operation has a "shape".
A data structure organises data so your common operations are fast. The right shape turns a hard task easy — and every structure trades speed in one operation for weakness in another.
To compare structures you need a way to talk about speed that doesn't depend on the machine. Big-O is that language: it measures how work grows as the data grows.
Big-O notation — growth, not seconds
Asking not how long one delivery takes, but how the total time changes as the route goes from ten stops to ten thousand — the shape of the growth, not a stopwatch reading.
Big-O notation describes how the cost of an operation grows as the amount of data, called n, grows. It ignores the exact milliseconds and the specific computer, and captures the shape: does doubling the data double the work, quadruple it, or barely change it? That shape decides whether something stays fast at scale, so Big-O is how engineers compare approaches without ever running them.
- What it is
- A description of how work grows as the data size n grows.
- How it works
Drop the exact seconds and hardware details; keep the shape of the growth.
- Example
"Searching an array is O(n)": ten times more items — roughly ten times more work.
- Gotcha
- It doesn't tell you how many seconds; it tells you how it scales.
- Related
- The common shapes are the Complexity classes.
Complexity classes — shapes from great to dreadful
Four ways a chore scales: one stays a single step no matter the pile, one grows with the pile, one explodes as the pile squared, and one barely grows even when the pile does.
A few shapes cover most code:
- O(1) — constant: the same time no matter the size (grabbing an item by index).
- O(log n) — logarithmic: barely grows even as data balloons (halving the search each step).
- O(n) — linear: grows in step with the data (scanning a list).
- O(n²) — quadratic: explodes (comparing every item to every other). The classic "fine in testing, dies in production."
For ten items, every structure feels instant — the differences don't show. Big-O is about what happens when n gets large. That's why things "suddenly" get slow in production: the bad shape was always there, just hidden until the data grew.
- What it is
- Names for the typical shapes of growth.
- Example
Array index — O(1); balanced-tree search — O(log n); scanning a list — O(n); a loop inside a loop — O(n²).
- Gotcha
O(n²) runs fine on test data and grinds for minutes on a million records.
- Rule
- O(1) and O(log n) survive scale; O(n) is honest; O(n²) is an alarm.
- Related
- Map before loop is the main way to turn O(n²) into O(n).
Big-O measures how work grows with the data, not raw seconds. O(1) and O(log n) survive scale; O(n) is honest; O(n²) is fine in testing and dies in production.
The most basic structure is the array, or list: an ordered sequence of items. Knowing exactly what it's fast and slow at sets the baseline for everything else.
Array — an ordered, numbered sequence
A row of numbered lockers: items sit in order, and each has a position — locker 0, locker 1, locker 2 — so you can go straight to any one by its number.
An array (or list) stores items in order, each at a numbered position called an index, starting from 0. Because the items sit in a known, contiguous layout, jumping to any position by its index is instant — O(1). Arrays are the default for "a bunch of things in order," and direct access by position is their superpower.
There's a price. Adding or removing at the end is cheap. But inserting or deleting in the middle shifts everything after it to keep the order and numbering intact — O(n).
- What it is
- Items in order, each with a numbered index from zero.
- Fast
- Access by index — O(1); appending at the end — cheap.
- Slow
- Search by value — O(n); insert or delete in the middle — O(n).
- Example
- A feed of posts read in order and appended to at the end.
- When to use
- You read by position and mostly append.
- Related
Its weak spot — finding by value — is what a Hash Map fixes; Stack and Queue are often built on top of it.
Linear search — finding by value
You can open locker 500 instantly — but if you only know what's inside and not which locker, you must open them one by one until you find it.
The catch: an array is fast only when you know the index. To find an item by its content — "is this email in the list?" — you have to check each one in turn. That's linear search, O(n): slow for large lists. That gap is exactly the problem the next structure solves.
- What it is
- Check items one by one until you find the one you want.
- Cost
- O(n): ten times more data — ten times longer.
- Example
emails.includes(x)on a list of a million addresses.- Gotcha
Inside another loop it becomes O(n²) — the classic "slow in production".
- Related
- Replaced by an O(1) lookup in a Hash Map or Set.
An array is an ordered, indexed sequence: instant access by position, but O(n) to search by value or insert in the middle.
If one structure deserves to be your default workhorse, it's the hash map. It solves the array's biggest weakness — finding things by value — and it does it almost instantly.
Hash Map — key-value pairs
A phonebook: you look up a person by name and instantly get their number. You don't scan every entry — the name takes you straight to the answer.
A hash map (also called a dictionary, hash table, or just map) stores data as key-value pairs: you give it a key, it gives you the matching value. A user by ID, a setting by name, a count by word — directly, by the key. Where an array forces you to know the position, a hash map lets you find by a meaningful name.
- What it is
- Key → value pairs with lookup by key.
- Fast
- Lookup, insert and delete by key — effectively O(1).
- Slow
- No order; finding "by value" rather than by key is a scan again.
- Example
users[id],settings["theme"], word countscounts[word] += 1.- When to use
- You constantly look things up by an identifier.
- Related
Its speed comes from the Hash function; without values it becomes a Set.
Hash function — why lookup is instant
A magic filing cabinet that, given a label, opens straight to the right drawer without checking the others — the number of drawers doesn't slow it down.
The magic of a hash map is that lookup by key is effectively O(1) — constant time, no matter how many items it holds. The key goes through a hash function that computes where the value lives, so the map jumps straight there instead of searching. A million entries or ten, finding one is just as fast. That's why the hash map is the workhorse: "find by name" is what programs do constantly.
- What it is
A function that computes, from a key, where its value sits in the table.
- How it works
The same key always gives the same slot — so lookup never scans the entries.
- Example
hash("eve")→ slot 3 → that's where Eve's number is.- Related
- The heart of Hash Map and Set.
Set — present or not, no values
A guest list at a door — you don't need any details, only a yes-or-no: is this name on the list? Instantly checked, no scanning.
A close cousin is the set: a hash map that stores only keys, no values, to answer one question fast — "is this thing present?" Checking membership and avoiding duplicates are both O(1). Whenever you find yourself scanning a list to ask "have I seen this already?", a set turns that slow O(n) check into an instant one.
- What it is
- A collection of unique keys with no values.
- Fast
- "Is it here?" and add — O(1); duplicates are dropped automatically.
- Example
seen = set()— skip emails already sent; remove repeats from a list.- When to use
- Uniqueness and fast membership tests.
- Related
- A Hash Map without the values.
A hash map finds a value by its key in effectively O(1) — the workhorse for lookup by identifier. Its cousin the set answers "is this present?" just as instantly.
Sometimes the order you take things out matters as much as what you store. Two structures formalise the two natural orders — and the difference between them is a single, intuitive rule.
Stack — last in, first out (LIFO)
A stack of plates: you add to the top and take from the top, so the last plate you put down is the first one you pick up.
A stack follows LIFO — last in, first out. You add (push) and remove (pop) at the same end, so the most recent item comes out first. This matches anything that nests or unwinds in reverse: an undo history, the back button, a program's call stack. Both operations are O(1).
- What it is
- Add and remove only at the top.
- Fast
pushandpop— O(1).- Example
- Undo in an editor, the browser back button, the call stack.
- When to use
- You need "most recent first" or "undo the last thing".
- Related
- Mirror of Queue: same data, other end for taking out.
Queue — first in, first out (FIFO)
A line at a ticket counter: people are served in the order they arrived, newcomers join the back, and the front is served next.
A queue follows FIFO — first in, first out. You add at the back (enqueue) and remove from the front (dequeue), so items are handled in arrival order. This is the structure for fairness and order: tasks to process, jobs waiting, requests to handle in turn — exactly the message-queue idea from the queues course. Like a stack, its operations are O(1).
- What it is
- Add at the back, take from the front.
- Fast
enqueueanddequeue— O(1).- Example
- Jobs for background workers, requests served in turn.
- When to use
- Order of arrival should be the order of handling.
- Related
- Mirror of Stack; at scale it grows into a message queue.
The rule determines the behaviour
Same pile of work, two doors: one hands back the most recent item, the other the oldest. The single choice of which end changes everything downstream.
Stacks and queues hold the same kind of data; the only difference is which end you remove from. Choosing between them is choosing the order your work comes back: reverse and nested (stack) or fair and sequential (queue). A small decision with a large effect — get the order right and the rest of the logic falls into place.
A stack is last-in-first-out — undo, nesting, reversing. A queue is first-in-first-out — fair, in-order processing. Same data; the end you remove from is the whole difference.
The last structures handle data that isn't a flat line but has shape — things that branch into hierarchies, or connect into networks. They're how you model relationships.
Tree — a hierarchy
A family tree or a company org chart: one thing at the top, branching down into children, each of which can branch further — structure that fans out, never loops back.
A tree organises data as a hierarchy: a root at the top, branching into nodes that have children, and so on down. It models anything nested — folders within folders, comments and their replies, a menu of categories. The branching shape mirrors real-world nesting, which makes trees the natural fit whenever your data is "things inside things." Most hierarchies you meet in software are trees underneath.
- What it is
- A hierarchy: every node can have children, but nothing loops back.
- Example
- Folders and files, a comment thread, a shop's category tree.
- When to use
- Your data is "things inside things".
- Gotcha
Squeeze a hierarchy into a flat list and you'll be gluing it back together by hand.
- Related
An ordered, even tree is a Balanced tree; a tree is a special case of a Graph.
Balanced tree — search in O(log n)
Finding a name in a phone book by opening the middle, then the middle of the right half, again and again — each step throws away half of what's left.
A well-organised, balanced tree gives a powerful property: you can search it in O(log n) by repeatedly halving the space, the way you find a word in a dictionary without reading every page. That logarithmic shape barely grows even as data explodes — a billion items is only about thirty steps. This is how databases keep lookups fast at huge scale: ordered data in tree form, searched by halving.
- What it is
- An ordered tree with the same depth on every side.
- How it works
At each node, compare and go into one half — the other half is never looked at again.
- Fast
- Search — O(log n): a billion items ≈ 30 steps.
- Example
- Database indexes: ordered keys in tree form.
- Related
- A special Tree; unlike a Hash Map, it keeps order.
Graph — a network of connections
A map of cities joined by roads, or people joined by friendships — things connected to each other in any pattern, with no top and no bottom.
A graph is the most general shape: nodes connected by edges, in any pattern, including loops. It models networks — social connections, roads between cities, links between pages, dependencies between tasks. Whenever the data is fundamentally "things related to other things" rather than a hierarchy or a line, it's a graph. Many hard, interesting problems — shortest route, who's connected to whom — are graph problems at heart.
- What it is
- Nodes and the edges between them — no hierarchy, loops allowed.
- Example
Friends in a social network, roads between cities, links between pages, task dependencies.
- Typical problems
- Shortest route; who is connected to whom.
- When to use
- The data is a network, not a line and not a hierarchy.
- Related
- A Tree is a graph with one root and no loops.
A tree models hierarchy and, when balanced, searches in O(log n) by halving. A graph models a network of connections. Use them when data has shape — nested or linked.
You rarely build these from scratch — your language provides them. The skill is choosing the right one for the job, and knowing enough Big-O to feel a bad choice before it bites.
Dominant operation — choose by what you do most
You stock the kitchen by what you cook nightly, not by what looks impressive.
The practical decision is almost always: what operation will I do most, and which structure makes that fast? Looking up by key, constantly? Hash Map. Need things in arrival order? Queue. Mostly reading by position? Array. Let the dominant operation pick the structure, and accept that it'll be slower at the things you rarely do. Optimise the common case; the structure is how.
- Idea
- The most frequent operation picks the structure, not habit.
- Example
Thousands of user-by-ID lookups per second → Hash Map, even though order is lost.
- Cost
- Rare operations get slower — a deliberate trade.
- Related
- Trade-off applied in practice.
Map before loop — reach for a map before a loop
Before searching a crowd face by face for someone, you check if there's a guest list — the list answers instantly what the search does slowly.
A hugely common, hugely effective move: when you catch yourself scanning a list to find or match things — an O(n) loop, or worse an O(n²) loop inside a loop — ask whether a hash map or set could make it instant. Replacing a nested search with a map lookup is one of the most frequent real-world speedups there is. The structure, not a faster loop, is usually the fix.
- Problem
- For each item we scan another list — O(n²), slow in production.
- Idea
- Build a Hash Map or Set once, then look up in it at O(1).
- Example
Instead of
users.find(u => u.id === order.userId)inside a loop over orders — buildbyIdfirst and usebyId[order.userId].- Cost
- A little memory for the map.
- Related
- Turns a Linear search inside a loop into O(1).
- What operation dominates — lookup, ordering, add/remove?
- Lookup by key? A Hash Map (or a Set, for presence) is O(1).
- Access by position, append-heavy? An Array fits.
- Order of removal matters — most recent (Stack) or oldest (Queue)?
- Nested or networked data — a Tree or a Graph?
- At scale, what's the Big-O — and is anything secretly O(n²)?
- You pick the structure by the operation you do most, not by habit.
- You reach for a Hash Map or Set instead of scanning a list to find or dedupe.
- You can name the Big-O of your hot path and you have no hidden O(n²).
- You use a Stack or Queue when removal order matters, and a Tree or Graph when data has shape.
- Things stay fast as the data grows, because you chose the shape before scale exposed it.
How it all connects
A family tree of tools: each one covers a weakness of the one before.
Big-O is the ruler every structure is measured with. A Hash Map fixes the Array's weak spot (finding by value); a Set is a Hash Map without values. Stack and Queue are a sequence with the rule "take only from an end". A Tree adds hierarchy, and a balanced one searches in O(log n); a Graph lifts the last restriction and allows any links.
Cheat sheet: all 16 concepts
Basics
- Data Structurehow data is laid out
A way of arranging data so your common operations are fast.
- Trade-offno free lunch
Strength in one operation costs weakness in another.
Big-O
- Big-O notationasymptotic complexity
How work grows as the data size n grows.
- Complexity classesO(1), O(log n), O(n), O(n²)
Constant, logarithmic, linear, quadratic. O(n²) is an alarm.
Array
- Arraylist
In order, with an index: O(1) by position, O(n) to insert in the middle.
- Linear searchscan
Find by value by checking one by one. O(n).
Hash map
- Hash Mapdictionary, map
Key → value, lookup by key effectively O(1). No order.
- Hash functionhashing
Computes from the key where the value lives — so no scanning.
- Setmembership
Keys only: "is it here?" in O(1), no duplicates.
Order
- StackLIFO
Last in, first out. Undo, back button, call stack.
- QueueFIFO
First in, first out. Processing in arrival order.
Shape
- Treehierarchy
Root, nodes, children, no loops.
- Balanced treebalanced search tree
Search by halving — O(log n). How database indexes work.
- Graphnetwork
Nodes and edges in any pattern. Networks, routes, dependencies.
How to choose
- Dominant operationcommon case
The most frequent operation picks the structure.
- Map before looplookup over scan
Before a nested scan — build a Hash Map or Set.
You don't build these from scratch — you choose them. Match the structure to the operation you do most, reach for a map before a loop, and let Big-O warn you before scale does.