Chapter 1: Introduction to Data Structures and Algorithms
1.01Beginner7 topics14 min2 interactive animations

Why learn DSA

Data structures and algorithms decide whether a program answers in milliseconds or never finishes. This lesson explains the vocabulary of problems, algorithms and contracts, and why correctness and efficiency are separate questions.

Learning goals

  • Distinguish a problem from the algorithms that solve it
  • State an input-output contract precisely
  • Explain why a correct solution can still be useless at scale
  • Name real systems that depend on well-chosen structures

Prerequisites

Basic programming in any language

A phone book with one million names can be searched by hand in about twenty page flips, if it is sorted. If it is not sorted, you may read every page. The data and the method matter more than how fast your hands are.

1.01.1

Problems vs algorithms

A problem is a precise question: “given an array of integers, return its largest value”. An algorithm is a finite, step-by-step procedure that solves it. One problem has many algorithms, and an algorithm is only meaningful relative to the problem it claims to solve.

Searching a sorted array is one problem with at least three algorithms: linear search, binary search and interpolation search. They return the same answers and differ in cost.

1.01.2

Data vs information

Data is raw, uninterpreted values: 72, "Ana", true. Information is data in context: “Ana's heart rate is 72 beats per minute”. Programs store data and derive information from it.

How data is organised decides which questions are cheap. The same 72 stored in a sorted array answers “is anyone at 72?” quickly; stored in an unsorted list it needs a scan.

1.01.3

Data structures vs abstract data types

An abstract data type (ADT) describes behaviour: what operations exist and what they promise, such as a Stack with push, pop and peek. A data structure is a concrete way to store the data so those operations work: a stack can be an array with a size counter or a linked list with a head.

Java mirrors this split: List is an ADT (an interface), while ArrayList and LinkedList are data structures that implement it. Code that depends on the interface can switch structures without changing.

ADT (what)Possible structures (how)
Listarray, linked list
Stackarray, linked list
Queuecircular array, linked list
Maphash table, balanced tree
Priority queuebinary heap, balanced tree
Java
1List<Integer> items = new ArrayList<>(); // ADT on the left, structure on the right
2items.add(7);
3items.get(0);
4// swap the structure, keep every line that uses `items`
5items = new LinkedList<>();
1.01.4

Input-output contracts

A contract states what the caller must supply (preconditions) and what the code guarantees in return (postconditions). “Given a sorted array and a target, return its index or -1” is a contract. Binary search is correct for this contract and wrong for unsorted input, so the sortedness belongs in the contract, not in a hope.

Write the contract before the code: input types and ranges, what counts as valid, what to return for “not found” or empty input, and whether the input may be modified.

Java
1/**
2 * Returns the index of target in arr, or -1 if absent.
3 * Precondition: arr is sorted ascending. Not modified.
4 * Postcondition: result == -1 or arr[result] == target.
5 */
6static int indexOf(int[] arr, int target) { ... }
1.01.5

Correctness vs efficiency

Correctness asks “does it give the right answer for every valid input?”. Efficiency asks “how much time and memory does it use as the input grows?”. They are independent: a brute-force solution can be perfectly correct and far too slow.

Compare the two solutions to the same two-sum problem. Both are correct. The first checks every pair; the second remembers what it has seen. Run both on the same input and compare the number of operations.

Brute force: check every pair

Interactive · edit the input

Your input

Try

int[] arr · length 6

7
11
2
15
4
9
012345
Being comparedMatch / answerOuter index
Setup

Try every pair (i, j) with i < j. There are n(n-1)/2 = 15 of them in the worst case.

Step 1 / 10
Java
1public static int[] twoSum(int[] nums, int target) {
2 for (int i = 0; i < nums.length; i++) {
3 for (int j = i + 1; j < nums.length; j++) {
4 if (nums[i] + nums[j] == target) {
5 return new int[] { i, j };
6 }
7 }
8 }
9 return new int[] { -1, -1 };
10}

Live state

0 pairs checked

target

13

n

6

No condition evaluated on this step
Execution trace (dry run table)Show 10 steps
StepLineStateWhat happens
1L2target=13 n=6Try every pair (i, j) with i < j. There are n(n-1)/2 = 15 of them in the worst case.
2L2target=13 i=0Fix i = 0 (value 7) and pair it with every later element.
3L4target=13 i=0 j=1 sum=18Not the target; try the next j.
4L4target=13 i=0 j=2 sum=9Not the target; try the next j.
5L4target=13 i=0 j=3 sum=22Not the target; try the next j.
6L4target=13 i=0 j=4 sum=11Not the target; try the next j.
7L4target=13 i=0 j=5 sum=16Not the target; try the next j.
8L2target=13 i=1Fix i = 1 (value 11) and pair it with every later element.
9L4target=13 i=1 j=2 sum=13This pair adds up to the target.
10L5return=[1, 2]return [1, 2] after 6 pair checks. Simple and obviously correct, but O(n²) in the worst case.

Optimised: remember what you have seen

Interactive · edit the input

Your input

Try

int[] arr · length 6

7
11
2
15
4
9
012345
Being comparedMatch / answerStored in the map

seen (value:index)

empty
Setup

A hash map answers “have I seen this value?” in O(1) on average, so one pass is enough.

Step 1 / 7
Java
1public static int[] twoSum(int[] nums, int target) {
2 Map<Integer, Integer> seen = new HashMap<>(); // value -> index
3 for (int i = 0; i < nums.length; i++) {
4 int need = target - nums[i];
5 if (seen.containsKey(need)) {
6 return new int[] { seen.get(need), i };
7 }
8 seen.put(nums[i], i);
9 }
10 return new int[] { -1, -1 };
11}

Live state

0 lookups

target

13

No condition evaluated on this step
Execution trace (dry run table)Show 7 steps
StepLineStateWhat happens
1L2target=13A hash map answers “have I seen this value?” in O(1) on average, so one pass is enough.
2L5i=0 nums[i]=7 need=613 - 7 = 6, which has not appeared yet.
3L8i=0 size=1Store 7 → 0 for future lookups.
4L5i=1 nums[i]=11 need=213 - 11 = 2, which has not appeared yet.
5L8i=1 size=2Store 11 → 1 for future lookups.
6L5i=2 nums[i]=2 need=1111 is in the map: it was stored at index 1, so that value plus 2 makes 13.
7L6return=[1, 2]return [1, 2] after 3 lookups: O(n) time and O(n) extra memory. Brute force needed O(1) memory but O(n²) time.
1.01.6

Scalability

Scalability is how cost grows as input grows. An algorithm that takes 1 ms for 1,000 items might take 1 ms × 1,000 = 1 s for a million items if it is linear, 11 days if it is quadratic, and longer than the age of the universe if it is exponential.

Move the slider below to see how the growth classes diverge. Small inputs hide the problem; large ones expose it.

How fast do the growth classes explode?

Interactive · drag the sliders

O(1)

array index, hash lookup

1 ops

instant

O(log n)

binary search

7 ops

instant

O(√n)

jump search, trial division

10 ops

instant

O(n)

one pass

100 ops

instant

O(n log n)

merge sort

664 ops

instant

O(n²)

nested loops, bubble sort

10,000 ops

10 µs

O(n³)

triple loop, Floyd-Warshall

10^6.0 ops

1.0 ms

O(2ⁿ)

all subsets

10^30.1 ops

10^14 years

O(n!)

all permutations

10^158 ops

10^141 years

Bars use a log scale (the bar shows the exponent of 10). Time assumes 10⁹ simple operations per second, a common rule of thumb for competitive and interview limits. Slide n up and watch O(n²) cross one second near n ≈ 30,000 and O(2ⁿ) become impossible before n reaches 60.

1.01.7

Real-world use cases

The structures in this course run underneath everyday systems. Choosing the right one is usually worth more than tuning code.

SystemStructure or algorithmWhy it fits
Database indexB-treeFew disk reads per lookup on sorted, large data
AutocompleteTrieWalk one character at a time along a shared prefix
Route planningGraph + Dijkstra with a heapShortest path over weighted roads
Undo/redoTwo stacksLast action undone first
Task schedulerPriority queueAlways run the most urgent task next
Cache eviction (LRU)Hash map + doubly linked listO(1) lookup and O(1) reordering
Spell checkerHash setAverage O(1) membership test

Practice ladder

Now try it yourself

Easy

  • Write the input-output contract for finding the maximum of an array
  • Name an ADT and two structures that can implement it

Medium

  • Give a correct but inefficient algorithm for the two-sum problem and state its cost
  • List three real systems and the structure each one relies on

Advanced

  • Show an example where two different algorithms satisfy the same contract but one violates a performance requirement

Key takeaways

  • A problem has many algorithms; the contract says what any solution must do.
  • An ADT is behaviour, a data structure is how it is stored.
  • Correctness and efficiency are separate: check both, in that order.