SECTION 1
Why Do We Care About Speed?
Imagine you wrote two JavaScript programs that both find a specific name in a list. One finishes in a blink; the other takes ten seconds. Both give the correct answer, but any user would prefer the faster one. Since the earliest days of computing, programmers have needed a way to talk about how fast code runs — not measured in seconds on a particular laptop, but in a universal way that works regardless of the machine. That universal language is called time complexity.
The idea didn't appear overnight. It grew out of decades of mathematical thinking about algorithms — step-by-step procedures for solving problems. Here are some key moments that led to the concept you'll learn today.
1936 • Turing Machines
1962 • Big-O Enters CS
1970s • Complexity Classes
Today • Everyday Relevance
The central question time complexity answers is simple: as the amount of data grows, how much more work does my code have to do? That question separates lightning-fast programs from ones that freeze your browser.
SECTION 2
Core Principles of Time Complexity
Before diving into code, you need a few foundational ideas. Time complexity is not about measuring clock time; it's about counting the number of basic operations your program performs relative to the size of its input. We call the input size n. A list of 10 items means n = 10; a list of 1,000 items means n = 1,000. The goal is to express the operation count as a function of n.
Input Size (n)
n represents how many items your code must process — for example, the length of an array.Operations, Not Seconds
Big-O Notation
Drop the Constants
Worst Case Focus
KEY TAKEAWAY
SECTION 3
Visualizing O(n) vs O(n²)
The best way to understand why nested loops are so much slower than single loops is to see their growth curves side by side. The diagram below plots the number of operations on the vertical axis against the input size n on the horizontal axis. Notice how the O(n) line rises steadily, while the O(n²) curve shoots upward dramatically once n gets large.
When n is small — say 5 or 10 — both approaches feel instant. But at n = 100, the single-loop approach does about 100 operations while the nested-loop approach does about 10,000. At n = 1,000, those numbers become 1,000 and 1,000,000. That explosive growth is exactly why understanding time complexity matters before your data gets large.
SECTION 4
Counting Operations — The Math Behind Big-O
You don't need calculus to understand the math here — just basic algebra. The key idea is to count how many times the innermost line of code executes as a function of n.
SINGLE LOOP
A for loop running from 0 to n − 1 executes its body exactly n times. We write this as O(n) — linear time.
NESTED LOOPS (SAME RANGE)
When an outer loop runs n times and an inner loop also runs n times for each iteration of the outer loop, the total is n × n = n². We write this as O(n²) — quadratic time.
CONSTANT TIME
Code that doesn't loop at all — like accessing arr[0] — runs in the same amount of time no matter how big n is. We call this O(1) — constant time.
Remember, Big-O always drops constant multipliers and lower-order terms. If your code does 3n + 5 operations, that simplifies to O(n). If it does 2n² + 10n + 7, that simplifies to O(n²) because the n² term dominates when n is large.
Why Drop Constants?
SECTION 5
Recognizing Time Complexity in JavaScript Code
Being able to look at a block of JavaScript and identify whether it's O(1), O(n), or O(n²) is the core skill you're building. The diagram below shows three common patterns you'll encounter in your programs.
| Pattern | Loop Structure | Big-O | n = 100 | n = 1,000 |
|---|---|---|---|---|
| No loop | Direct access / assignment | O(1) | 1 | 1 |
| Single loop | for (let i...) | O(n) | 100 | 1,000 |
| Nested loops | for (i...) { for (j...) } | O(n²) | 10,000 | 1,000,000 |
Operation counts for common loop structures
A quick rule of thumb: count the depth of nested loops that depend on n. Zero loops → O(1). One loop → O(n). Two nested loops → O(n²). Three nested loops → O(n³). This pattern continues, though in practice you'll rarely see more than two nested loops in introductory programming.
SECTION 6
Worked Example — Finding Duplicates Two Ways
Let's compare two JavaScript functions that both check whether an array contains any duplicate values. We'll analyze the time complexity of each approach step by step.
Approach A — Nested Loops (Brute Force)
This approach compares every element to every other element using two for loops:
function hasDuplicateA(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) return true;
}
}
return false;
}
Analyzing Approach A
- 1
Step 1 — Identify the Loops
There is an outer loop (variablei) that runs n times, and an inner loop (variablej) that runs up to n − 1 times for each iteration of the outer loop. - 2
Step 2 — Count Total Comparisons
In the worst case (no duplicates), the inner loop runs (n − 1) + (n − 2) + ... + 1 + 0 times. This is the classic sum formula: n × (n − 1) / 2, which simplifies to roughly n² / 2. - 3
Step 3 — Apply Big-O Rules
Drop the constant ½ and the lower-order term. The result is O(n²).Time complexity: O(n²)
Approach B — Single Loop with a Set
This approach uses a JavaScript Set to remember values we've already seen:
function hasDuplicateB(arr) {
const seen = new Set();
for (let i = 0; i < arr.length; i++) {
if (seen.has(arr[i])) return true;
seen.add(arr[i]);
}
return false;
}
Analyzing Approach B
- 1
Step 1 — Identify the Loops
There is only oneforloop. It iterates through the array once. - 2
Step 2 — Count Operations per Iteration
Inside the loop,seen.has()andseen.add()each take roughly O(1) time on average (a property of Sets). So each iteration costs a constant amount of work. - 3
Step 3 — Apply Big-O Rules
One loop × constant work per iteration = n × 1 = n. The result is O(n).Time complexity: O(n)
For an array of 10,000 elements, Approach A might perform up to about 50,000,000 comparisons, while Approach B performs at most 10,000. Approach B is dramatically faster for large inputs, thanks to trading a nested loop for a data structure (the Set) that offers constant-time lookups.
SECTION 7
Strengths, Limitations & Tradeoffs
Understanding time complexity helps you choose better approaches, but it's not the only thing that matters. Here's a comparison of common tradeoffs.
| Factor | O(n²) Nested Loops | O(n) Single Loop + Data Structure |
|---|---|---|
| Speed at large n | Very slow — operations grow rapidly | Fast — operations grow proportionally |
| Speed at small n | Fine — both feel instant | Fine — both feel instant |
| Memory usage | Low — no extra storage needed | Higher — needs a Set or object to store seen values |
| Code simplicity | Very readable — just two loops | Slightly more complex — requires knowledge of Sets |
| When to use | Tiny arrays, learning/prototyping | Production code, large datasets |
Comparing nested-loop vs single-loop approaches
KEY TAKEAWAY
It's also worth noting that Big-O describes the worst case. In practice, an O(n²) algorithm might finish early if it finds what it's looking for. But when you're comparing approaches, you should plan for the worst case, because that's when the difference matters most.
SECTION 8
Connection to More Advanced Complexity
The O(1), O(n), and O(n²) categories you've learned today are just the beginning. As you progress in computer science, you'll encounter more complexity classes. Here's a preview of where this topic goes.
| What You Know Now | What Comes Next |
|---|---|
| O(1) — constant time | Still O(1) — this stays the same. Hash table lookups are the classic example. |
| O(n) — single loop | O(log n) — algorithms like binary search that cut the problem in half each step. Even faster than O(n). |
| O(n²) — nested loops | O(n log n) — efficient sorting algorithms like merge sort sit between O(n) and O(n²). |
| Counting operations by hand | Space complexity — analyzing how much memory an algorithm uses, using the same Big-O notation. |
| Worst-case analysis only | Average-case and best-case analysis — different Big-O for different scenarios of the same algorithm. |
How introductory time complexity connects to advanced topics
Don't worry about memorizing all of these right now. The important foundation is this: you can look at a piece of code, count its loop nesting depth, and express how its work grows as a function of n. That skill will serve you in every computer science course and coding interview you encounter.
Fun Fact
SECTION 9
Practice Problems
Question 1: CONCEPTUAL
Show answer
Question 2: BASIC CALCULATION
function sumArray(arr) {
let total = 0;
for (let i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}Show answer
for loop that iterates through every element of the array once. Inside the loop, the operation total += arr[i] is a constant-time step. So the total work is n iterations × 1 operation = n, giving us O(n). The let total = 0 and return total are each O(1) and don't affect the overall classification since n dominates.Question 3: INTERMEDIATE
function printTwice(arr) {
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
for (let j = 0; j < arr.length; j++) {
console.log(arr[j] * 2);
}
}Show answer
Question 4: APPLIED
Show answer
Question 5: CRITICAL THINKING
function binarySearch(sortedArr, target) {
let low = 0;
let high = sortedArr.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
if (sortedArr[mid] === target) return mid;
else if (sortedArr[mid] < target) low = mid + 1;
else high = mid - 1;
}
return -1;
}Show answer
SUMMARY
Lesson Summary
Summary
Time complexity measures how the number of operations in your code grows as the input size n increases. We express it using Big-O notation, which drops constants and lower-order terms to focus on the growth pattern. Code with no loops runs in O(1) constant time. A single loop that processes each element once runs in O(n) linear time. Nested loops that compare every element to every other element run in O(n²) quadratic time.
When comparing two approaches that solve the same problem, the one with lower time complexity will perform dramatically better as data grows. However, the time-space tradeoff means faster algorithms sometimes use more memory. The practical skill to build is simple: look at your code, count the depth of nested loops that depend on n, and express the growth rate. Zero loops = O(1), one loop = O(n), two nested loops = O(n²). Master this, and you're ready for more advanced algorithm analysis.