Tags

Theory

Theory

10 lessons

theory

Foundations

9 lessons

Amortized Analysis

Advanced

75 min

2 prereqs

Appending to a Python list or JavaScript array almost always feels instantaneous, yet every so often the structure allocates a fresh buffer and copies every element into it, an `O(n)` operation. So why do textbooks still call append `O(1)`? Because amortized over a long sequence of appends, the rare expensive operation is paid for by many cheap ones. **Amortized Analysis** is the formal machinery that makes this kind of argument rigorous. This lesson teaches the three classical methods. The **aggregate method** sums the total cost of `n` operations and divides by `n` for a per-operation bound. The **accounting (banker's) method** charges each operation a fixed amount and stores credits on the data structure to pay for future expensive ones. The **potential (physicist's) method** defines a potential function `Phi` and computes amortized cost as actual cost plus the change in `Phi`. You will apply all three to dynamic array doubling, stacks with multipop, and binary counters, and see how amortized `O(1)` differs from worst-case `O(1)` and from average-case analysis. This lesson stands on the shoulders of **Big-O Notation (Upper Bound)** and **Time Complexity Analysis Techniques**. From those lessons you can already analyze a single operation; amortized analysis is what you reach for when the cost of *one* operation is misleading and the right object of study is a *sequence* of operations. Next you will move to **Recurrence Relations**, the other major analysis tool, used when an algorithm's cost is most naturally written recursively rather than as a running total.

Not Started

0%

Foundations
Advanced
Premium
Amortized Analysis
Time Complexity
Analysis Techniques
Big-O
Theory
Deep Dive

Asymptotic Analysis Fundamentals

Free
Beginner

40 min

1 prereq

How can you tell which of two algorithms will scale to a million inputs without ever running them on real hardware? Wall-clock timing fails this test because it depends on your CPU, your language, and even what other apps are open, so we need a more durable measure of efficiency. The word *asymptotic* looks intimidating, but the idea behind it is simple: it describes how a function behaves as its input grows very large. **Asymptotic Analysis Fundamentals** uses that idea to build a hardware-independent measure of efficiency — by counting how the number of operations an algorithm performs grows as the input size `n` increases, you get a yardstick that works regardless of your CPU, language, or environment. You will see why basic operation counting beats stopwatch timing, how growth rates rank from constant to exponential, why constant factors and lower-order terms drop out of the analysis, and what it really means for `n` to represent the size of an input. In **Counting Operations**, you built the habit of tracing through code and tallying how many steps it performs as input size `n` grows. Every operation count you produced there — a loop running `n` times, nested loops running `n * n` times — becomes exactly the formula this lesson formalizes. Asymptotic analysis is the language that turns those raw counts into a precise, hardware-independent way to compare algorithms. Once you are comfortable thinking in growth rates, you will be ready for **Big-O Notation (Upper Bound)**, the formal symbolic language (think `O(n)`, `O(n^2)`, `O(log n)`) used everywhere in textbooks, interviews, and engineering documentation to express the ideas you build here.

Not Started

0%

Foundations
Beginner
Free
Asymptotic Analysis
Time Complexity
Efficiency
Fundamentals
Theory

Basic Proof Techniques

Advanced

65 min

1 prereq

Goldbach's conjecture has been verified for every even integer up to 4 * 10^18, yet mathematicians still call it a conjecture because no amount of testing covers every case. Algorithm correctness has the same shape: a function can pass a hundred unit tests and still fail on the empty list or the input size your tests never reached. **Basic Proof Techniques** equips you with the small set of argument styles that turn intuition into certainty about correctness, complexity bounds, and data structure invariants. Direct proof chains definitions and prior facts to a claim. Proof by contradiction assumes the opposite and derives a logical impossibility, as in the irrationality of `sqrt(2)` and the `Omega(n log n)` lower bound for comparison sorts. Mathematical induction, in weak and strong forms, proves statements over the natural numbers via a base case and an inductive step, the backbone of recurrence solutions and loop invariants. Proof by contraposition swaps `P implies Q` for the equivalent `not Q implies not P`, and proof by cases breaks a claim into exhaustive subcases. This lesson builds on **Discrete Mathematics Basics**, where you met sets, propositional logic, implications, and the first taste of formal proof. Here those informal sketches become the disciplined patterns that algorithm analysis, theoretical CS, and clean engineering arguments all rely on. With proofs in hand, you have completed the foundations track and are ready to step into **Arrays & Strings** and the rest of the data structures curriculum, armed to analyze every operation rigorously.

Not Started

0%

Foundations
Advanced
Premium
Proof Techniques
Mathematical Induction
Discrete Mathematics
Mathematics
Correctness
Theory

Big-Omega Notation (Lower Bound)

Free
Intermediate

40 min

1 prereq

If `O(n)` is the worst-case ceiling on an algorithm's running time, what tells you the floor, the absolute minimum work the algorithm must always do? That is the question **Big-Omega Notation** answers, and it is the second pillar of asymptotic analysis that turns half a description of performance into the full picture. This lesson introduces `Omega(g(n))` as a formal lower bound, with the mirror-image definition of Big-O: there exist positive constants `c` and `n0` such that `f(n) >= c * g(n)` for all `n >= n0`. You will practice proving Omega claims, analyze the best-case behavior of familiar algorithms (linear search, bubble sort, insertion sort), and meet a famous problem lower bound: any comparison-based sort must do at least `Omega(n log n)` comparisons in the worst case. You will also learn to spot when an algorithm is asymptotically optimal because its upper and lower bounds coincide. This lesson builds directly on **Big-O Notation (Upper Bound)**, where you learned to express upper bounds on growth using constants `c` and `n0`. Big-Omega flips the inequality from `<=` to `>=` and reuses the same machinery to describe floors instead of ceilings. Once you are comfortable bracketing algorithms with both bounds, you will be ready for **Big-Theta Notation (Tight Bound)**, where the upper and lower bounds meet to give the most precise complexity statement possible.

Not Started

0%

Foundations
Intermediate
Free
Big-Ω (Omega)
Asymptotic Analysis
Time Complexity
Best/Worst/Average Case
Theory
Comparison

Big-Theta Notation (Tight Bound)

Free
Intermediate

35 min

2 prereqs

When you can say *both* that an algorithm runs in `O(n log n)` and that it must do at least `Omega(n log n)` work, the ceiling and floor have collapsed onto the same growth rate. That coincidence is the most informative complexity statement you can make, and it is what **Big-Theta Notation** captures in a single symbol. This lesson defines `Theta(g(n))` as the intersection of Big-O and Big-Omega: there exist positive constants `c1`, `c2`, and `n0` such that `c1 * g(n) <= f(n) <= c2 * g(n)` for all `n >= n0`, the so-called sandwich property. You will practice classifying functions and algorithms by their tight bound, learn when Big-Theta is appropriate (best and worst case have the same growth rate) versus when only Big-O is honest (the cases differ), and see why merge sort is `Theta(n log n)` while linear search is *not* Theta of any single function. This lesson stitches together two ideas you have already met. From **Big-O Notation (Upper Bound)** you have the ceiling `f(n) <= c * g(n)`, and from **Big-Omega Notation (Lower Bound)** you have the floor `f(n) >= c * g(n)`. Big-Theta simply requires both bounds to hold for the same `g(n)`, so an algorithm only earns a Theta label when its upper and lower bounds genuinely match. With all three asymptotic notations in hand, you will be ready to switch focus to **Memory Models**, where the same growth-rate thinking gets applied to space rather than time.

Not Started

0%

Foundations
Intermediate
Free
Big-Θ (Theta)
Asymptotic Analysis
Time Complexity
Big-O
Big-Ω (Omega)
Theory
Comparison

Combinatorics Basics

Free
Intermediate

60 min

1 prereq

How many ways can you arrange `n` items? How many subsets does a set of size `n` have? Questions like these are not idle puzzles, they are exactly the questions you have to answer to know whether a brute-force solution will run in milliseconds or in millennia. **Combinatorics Basics** gives you the counting tools that turn vague statements like "try every possibility" into precise complexity claims. This lesson covers the four counting ideas you will rely on throughout DSA. The **rule of sum** and **rule of product** let you decompose multi-step processes and count outcomes; **permutations** count ordered arrangements (`n!` for arranging all of them, `nPr` for choosing `r` in order); **combinations** count unordered selections (`nCr`, also written as binomial coefficients); and **Pascal's triangle** plus a first look at **basic probability** tie everything together. You will see why a set of size `n` has `2^n` subsets, why generating all permutations of an array is `O(n!)`, and how counting principles directly produce the time complexity of backtracking algorithms. This lesson builds on **Discrete Mathematics Basics**, where you formalized sets, set operations, and logic. Combinatorics is what happens when you start asking "how many?" about those sets, ordered or unordered, with or without repetition. With the counting toolkit in place you will continue the math track in **Number Theory Fundamentals**, picking up primes, divisibility, and `gcd`, the other half of the math you need for hashing, cryptography-style problems, and modular arithmetic.

Not Started

0%

Foundations
Intermediate
Free
Combinatorics
Probability Basics
Mathematics
Discrete Mathematics
Fundamentals
Theory

Complexity Classes (Conceptual Overview)

Advanced

65 min

5 prereqs

Some problems have algorithms that finish in seconds on inputs of size a million. Others, like finding the shortest tour through 30 cities, can defeat the world's fastest computers no matter how cleverly you code. The boundary between these two worlds is studied formally in **complexity theory**, and the rough map of that boundary is given by **complexity classes**: `P`, `NP`, `NP-Complete`, and `NP-Hard`. This lesson introduces those classes conceptually. You will see how `P` collects problems solvable in polynomial time, how `NP` collects problems whose solutions can be *verified* in polynomial time (even if finding them might be slow), and how `NP-Complete` and `NP-Hard` capture the hardest problems in `NP` and beyond. You will meet famous examples (Traveling Salesman, SAT, the knapsack problem), get a light introduction to **polynomial-time reductions** as the tool used to prove a new problem is `NP-Complete`, and confront the legendary `P vs NP` question along with why it matters for cryptography and the entire structure of computer science. This lesson assumes you are fluent with all the basic asymptotic machinery from **Asymptotic Analysis Fundamentals**, **Big-O Notation (Upper Bound)**, **Time Complexity Analysis Techniques**, **Space Complexity Fundamentals**, and **Little-o and Little-omega Notations**. Complexity classes use those same growth-rate ideas to draw lines between "polynomial" and "exponential" rather than between specific functions like `n^2` and `n log n`. From here you will move into **Amortized Analysis**, shifting from broad problem classification back to the fine-grained per-operation analysis that complex data structures demand.

Not Started

0%

Foundations
Advanced
Premium
Complexity Classes (P/NP)
Time Complexity
Big-O
Theory
Deep Dive
Efficiency

Discrete Mathematics Basics

Free
Intermediate

55 min

Almost every conditional you have ever written is a tiny piece of propositional logic, and almost every collection you have ever stored is, mathematically, a set. **Discrete Mathematics Basics** makes that connection explicit, giving you the formal vocabulary that data structures, graph algorithms, database queries, and correctness arguments all sit on top of. This lesson walks you through three pillars of discrete math used throughout DSA. First, **sets** and the operations on them: union, intersection, difference, complement, subset, and membership, the same ideas baked into hash sets and array problems. Second, **propositional logic**: the operators `AND`, `OR`, `NOT`, and implication, plus truth tables for evaluating compound expressions; this is the math behind every Boolean condition in your code. Third, **binary relations** and their properties (reflexive, symmetric, transitive), which generalize the way edges connect nodes in graphs and rows connect in databases. Along the way you will get a first taste of formal proof: how to argue that two sets are equal or that a logical equivalence always holds. This lesson assumes only basic programming familiarity, so no specific DSA prerequisite is required. If you have written `if (x && !y)` or used a hash set, you already have informal intuition for everything we will formalize here. From here you will move into **Combinatorics Basics**, where you will use these set and counting foundations to count permutations, combinations, and arrangements (the math behind backtracking and many complexity proofs).

Not Started

0%

Foundations
Intermediate
Free
Discrete Mathematics
Sets & Logic
Mathematics
Proof Techniques
Fundamentals
Theory

Little-o and Little-omega Notations

Advanced

55 min

3 prereqs

When a paper claims an algorithm runs in `O(n^2)`, that statement leaves a question wide open: does the algorithm *actually* take quadratic time, or could the bound be loose? **Little-o** and **little-omega** are the notations that close that gap. `f(n) = o(g(n))` says `f` grows *strictly* slower than `g`, never matching its rate, and `f(n) = omega(g(n))` says `f` grows strictly faster. They are the precision tools of theoretical CS. This lesson defines both notations using the formal limit-based criterion: `f(n) = o(g(n))` iff `lim f(n)/g(n) = 0`, and `f(n) = omega(g(n))` iff `lim f(n)/g(n) = infinity`. You will prove and disprove `o` and `omega` claims for common function pairs (`n` vs `n^2`, `log n` vs `n`, `n` vs `n log n`, `2^n` vs `n^k`), use these notations to rank growth rates strictly, and see how the full five-notation system fits together: `O` and `Omega` allow equality, `Theta` requires it, while `o` and `omega` forbid it. This lesson builds on three earlier asymptotic foundations. From **Big-O Notation (Upper Bound)** you have non-strict ceilings; from **Big-Omega Notation (Lower Bound)** you have non-strict floors; and from **Big-Theta Notation (Tight Bound)** you have the exact-rate case. Little-o and little-omega simply require the inequality to be strict, ruling out the matching case that Big-O and Big-Omega permit. With all five notations under your belt, you will be ready for **Complexity Classes (Conceptual Overview)**, where these comparisons get used to separate classes like `P` and `NP`.

Not Started

0%

Foundations
Advanced
Premium
Little-o
Little-ω
Asymptotic Analysis
Big-O
Big-Ω (Omega)
Theory
Comparison

Algorithms

1 lesson

Complexity & Proof Intuition (Optional)

Advanced

50 min

3 prereqs

Comparison-based sorting cannot do better than `O(n log n)`. That is not a statement about merge sort or quick sort or any specific algorithm; it is a statement about every possible algorithm in this model, including ones nobody has invented yet. The proof is a counting argument over decision trees, and learning to read it changes how you reason about algorithmic limits forever. Lower bounds, NP-completeness, P versus NP, and loop invariants all live in the same theoretical neighborhood. **Complexity & Proof Intuition (Optional)** is an enrichment tour of that neighborhood. You will use loop invariants to prove iterative algorithms correct, revisit amortized analysis with the potential method, work through NP-completeness via polynomial-time reductions and the Cook-Levin theorem, examine the P vs. NP question and its implications, derive the `Omega(n log n)` comparison-sorting lower bound via decision trees, and meet the broader complexity zoo (co-NP, PSPACE, EXP). This lesson rests on three earlier ideas. **Asymptotic Analysis Fundamentals** gave you the language of growth rates that complexity classes refine. **Dynamic Programming (Intro)** and **Greedy (Intro)** showed you exact polynomial-time algorithms; this lesson asks which problems will never have one. This concludes the algorithms track. From here, the natural next step is sustained problem solving on hard interview problems and competitive programming contests that exercise the full toolkit you have built.

Not Started

0%

Algorithms
Complexity Classes (P/NP)
Proof Techniques
Invariants
Theory
Advanced
Premium
Correctness