v9.1.1
Menu

Chapter 1: Fundamentals

Week 1

Master the building blocks of data structures and algorithms: understand time complexity, implement essential data structures like stacks, queues, and hash maps, then conquer the most important sorting algorithms and learn how to customize them for any use case.

Topics
7
Videos
7
Min read
37

Chapter Overview

Every great programmer stands on a foundation of data structures and algorithms. Before you can solve complex problems, you need to understand how data is organized, how operations perform at scale, and which tools to reach for in different situations.

This chapter starts with Big O notation --- the universal language for reasoning about efficiency. You will learn to look at code and immediately estimate whether it will run in milliseconds or hours. Then you will build the fundamental data structures that appear in nearly every technical interview and production codebase: stacks, queues, and hash maps.

The second half focuses on sorting, one of the most studied problems in computer science. You will implement the simple quadratic sorts to understand the basics, then level up to merge sort and quick sort --- the divide-and-conquer algorithms that power real-world systems. Finally, you will learn to write custom comparators so you can sort anything by any criteria.

This chapter covers:

  • Big O Notation: Analyzing time and space complexity
  • Stacks & Queues: LIFO and FIFO data structures
  • Hash Maps: O(1) lookups with key-value pairs
  • Elementary Sorting: Bubble, selection, and insertion sort
  • Advanced Sorting: Merge sort and quick sort
  • Custom Comparators: Sorting with flexible criteria

Chapter Roadmap

Click any topic to jump in

Explainer video
1
Data Structures & Big O

The mental model: choosing how to organize data determines which operations are cheap. Big O lets you compare choices before you code.

What Are Data Structures?Big O NotationSpace Complexity
Complexity drives choice of structure

Stacks and queues are the two simplest linear structures built on arrays or linked lists.

2
Stacks (LIFO)

Last-in-first-out storage that mirrors the call stack and solves matching problems like valid parentheses.

LIFO Principle and Core OperationsThe Call StackValid Parentheses Problem
3
Queues (FIFO)

First-in-first-out storage that powers BFS, level-order traversal, and rate limiting via sliding windows.

FIFO Principle and Core OperationsBFS and Level-Order TraversalRecent Counter Problem
From linear scans to hashed lookups
4
Hash Maps

Average-case $O(1)$ lookups via hashing — the workhorse of counting, deduping, and complement lookups.

Key-Value Storage and Hash FunctionsCollision HandlingTwo Sum Using Hash Map
From storage to ordering

Once you can store and retrieve, the next question is how to order data efficiently.

5
Sorting Basics

Bubble, selection, insertion — the $O(n^2)$ baseline. Understanding stability matters more than raw speed here.

Bubble SortSelection SortInsertion SortStability in Sorting
6
Merge & Quick Sort

Divide and conquer sorts that reach $O(n \log n)$ — merge sort is stable, quick sort is faster in practice.

Divide and ConquerMerge SortQuick Sort
From $O(n^2)$ to $O(n \log n)$
7
Custom Comparators

Sorting by arbitrary criteria — key functions, multi-key, and the strict-weak-ordering contract.

Sorting with Key FunctionsMulti-Key SortingCustom Comparison Functions

Topics

7 topics in this chapter

Suppose you hold ten million user IDs and must answer, thousands of times per second, whether a given ID is banned. Scan a plain list and each query touches up to ten million entries; the service falls over the moment traffic spikes. Nothing is wrong with the loop itself. The problem is how the data is laid out.

As the first topic of the DSA plan, this one has no predecessor, so it builds the vocabulary every later topic relies on. A data structure is a layout plus a set of operations with known costs, and the craft is matching the layout to the operations your program performs most often. To compare layouts before writing code, we need a machine-independent measure of cost. Big O notation provides it: it describes how work grows as the input size n grows, ignoring constant factors and hardware.

We first look at what a data structure actually promises, then learn to read time complexity straight off loops, and finally measure the extra memory an algorithm needs. Stacks, queues, and hash maps in the next topics are all judged with exactly these tools.

Definition

A data structure is a concrete organization of data in memory together with the operations it supports and the cost of each. Big O notation, written f(n)=O(g(n))f(n) = O(g(n)), states that for all sufficiently large inputs the cost f(n) is at most a constant multiple of g(n), so it classifies algorithms by growth rate rather than by exact running time.

In this topic

  1. 1What Are Data Structures?
  2. 2Big O Notation
  3. 3Space Complexity
1 of 3
What Are Data Structures?

Every program repeatedly asks a few questions of its data: is this item present, which is the smallest, what arrived last. A data structure is a commitment to answer some of those questions cheaply at the expense of others. An array stores elements contiguously, so reading index i is O(1), but inserting at the front shifts every element, costing O(n). A linked list inverts that trade. A hash map answers membership in O(1) on average but forgets order entirely. The failure mode is choosing by habit: a list used for millions of membership checks silently turns a linear-time job into a quadratic one.

Mathematical Intuition

A data structure is a triple (S,Ops,Costs)(S, \text{Ops}, \text{Costs}) where SS is the set of states, Ops\text{Ops} are the supported operations, and Costs:Ops→R≥0\text{Costs}: \text{Ops} \to \mathbb{R}_{\ge 0} is the cost model. Choosing a structure means minimising ∑ifi⋅c(opi)\sum_{i} f_i \cdot c(op_i) where fif_i is how often each operation is performed. For a workload with NN lookups and MM inserts, a hash set costs O(N+M)O(N + M) while a sorted array costs O(Nlog⁡N+M⋅N)O(N \log N + M \cdot N) — the right choice can change the total by a polynomial factor.

Example:

Compare the time complexity of finding an element in an unsorted list vs. a sorted list vs. a hash set. Which would you choose if you need to check membership millions of times?

2 of 3
Big O Notation

Big O answers how the work grows when the input doubles, not how many seconds a program takes. Formally, f(n)=O(g(n))f(n) = O(g(n)) means that beyond some input size n0n_0 the cost f(n)f(n) never exceeds c⋅g(n)c \cdot g(n) for a fixed constant cc. Here n is the input size, f counts basic operations, and g is a simple reference curve such as n or n^2. To analyse code, count how often the innermost statement runs: one loop over n gives O(n), two nested loops give O(n^2), and halving the range each step gives O(log n). The classic trap is hidden work inside a call, like x in some_list, which is itself O(n).

Mathematical Intuition

f(n)=O(g(n))f(n) = O(g(n)) iff there exist constants c>0c > 0 and n0≥0n_0 \ge 0 such that 0≤f(n)≤c⋅g(n)0 \le f(n) \le c \cdot g(n) for all n≥n0n \ge n_0. This definition ignores constants and lower-order terms because for any polynomial ank+bnk−1+…a n^k + b n^{k-1} + \dots, the dominant term eventually dwarfs the rest: lim⁡n→∞ankank+bnk−1=1\lim_{n \to \infty} \frac{a n^k}{a n^k + b n^{k-1}} = 1. Big O is an upper bound on growth, giving a machine-independent way to compare algorithms.

Example:

What is the time complexity of the following code?

for i in range(n):
    for j in range(n):
        print(i, j)
3 of 3
Space Complexity

Time is not the only budget: a program that is fast but needs more memory than the machine has simply crashes. Space complexity counts the extra memory an algorithm allocates as a function of the input size n, excluding the input itself. Local variables and fixed counters cost O(1); a new list proportional to the input costs O(n); recursion costs one stack frame per active call, so recursion depth d costs O(d). Insertion sort rearranges in place with O(1) extra space, while merge sort needs an O(n) buffer. The hidden failure mode is recursion, which consumes stack space even when no data structure is visibly allocated.

Mathematical Intuition

Space complexity counts the extra memory used as a function of input size nn, excluding the input itself. For a recursive algorithm with recursion depth dd and local variables of size ss, space is O(d⋅s)O(d \cdot s) because the call stack holds dd frames simultaneously. Example: merge sort uses O(n)O(n) auxiliary space for the merge buffer plus O(log⁡n)O(\log n) for recursion, totalling O(n)O(n).

Example:

What is the space complexity of creating a new list that contains the squares of every element in the input list?