Skip to content
SeriesProgramming

DSA & Algorithms in Python

A 52-part course through data structures and algorithms, every one implemented and explained in Python.

51 parts17h totalMar 20, 2026 — Aug 9, 2026
  1. What Is DSA? Data Structures and Algorithms Explained for Complete BeginnersWhat data structures and algorithms actually are, why the wrong structure costs a factor of a million, an intuitive first look at Big O, and which language to learn it all in.18 min
  2. Python From Zero: The Complete Beginner's Guide to the LanguagePython from zero: where it came from, how to install it, and every part of the core language, plus what the language is really used for and which editor to learn in.46 min
  3. Big O Notation: How to Measure an Algorithm Without Running ItBig O from the counting argument up: what the formal definition promises, why constants vanish, how to read a bound off code, and the Master Theorem worked through.25 min
  4. Bubble Sort in Python: The Algorithm Everyone Learns FirstBubble sort explained by counting its comparisons: how the passes work, where the n squared comes from, the one boolean that makes its best case linear, and what to use instead.13 min
  5. Selection Sort in Python: The Fewest Swaps of Any Simple SortSelection sort makes exactly n-1 swaps on any input, the lowest worst case of any simple sort. Why that matters, why it is neither stable nor adaptive, and how heap sort fixes it.17 min
  6. Insertion Sort in Python: The Fast One Nobody ExpectsInsertion sort by the numbers: shifts equal inversions, which is why sorted input costs one linear pass, nearly-sorted input costs O(n times k), and Timsort still uses it.16 min
  7. Merge Sort in Python: Guaranteed O(n log n), Every Single TimeMerge sort explained by counting its work: the split, the merge that does all the real sorting, why every input costs n log n, and how it sorts files larger than memory.16 min
  8. Quick Sort in Python: The Fastest Sort in Practice, and Its One WeaknessHow partitioning works, why Lomuto and Hoare differ, and why the pivot rule decides everything: 2,698 comparisons or 79,800 on the same 400 items.14 min
  9. Heap Sort in Python: O(n log n) Without Using Extra MemoryHow heap sort builds a max-heap inside the array itself, why the bottom-up build is O(n) rather than O(n log n), and why quick sort still beats it in practice.18 min
  10. Counting Sort in Python: Sorting Without Comparing AnythingCounting sort never compares two items. Tally each key, turn the tally into output positions with a prefix sum, place every item — O(n + k), stable, and useless when k is big.22 min
  11. Radix Sort in Python: Sorting Digit by Digit, Faster Than O(n log n)How sorting one digit at a time beats the comparison bound: why every pass must be stable, why the passes run least significant first, and when it actually wins.14 min
  12. Timsort: The Algorithm Behind Python's sorted() and list.sort()What Python's sorted() actually runs: natural runs, minrun, binary insertion sort, the merge-stack invariants and galloping mode, with a runnable simplified Timsort.22 min
  13. Linear Search in Python: The Simplest Algorithm, and When It's Still RightLinear search scans until it finds a match: O(1) at best, exactly n comparisons on a miss, and the only option on unsorted data. Plus the maths for when sorting first pays off.15 min
  14. Binary Search in Python: Halving the Problem Every StepBinary search halves the range on every probe: the sorted-data precondition, the boundary traps that hang the loop, Python's bisect module, and searching on the answer.22 min
  15. Arrays and Dynamic Arrays: How Python Lists Actually WorkWhy contiguous memory gives O(1) indexing, how a dynamic array grows, the amortised O(1) append argument done properly, and why insert(0, x) is O(n).17 min
  16. Linked Lists in Python: Singly, Doubly, and Circular, From ScratchSingly, doubly and circular linked lists built from scratch in Python, with the three-pointer reversal, Floyd's cycle detection, and an honest comparison against list.18 min
  17. Stacks in Python: Last In, First Out, and Where You Already Use OneA stack is a pile you only ever touch at the top. How push, pop and peek stay constant time on a plain Python list, and the five places you already depend on one.18 min
  18. Queues and Deques in Python: FIFO, Ring Buffers, and collections.dequeWhy list.pop(0) costs O(n) and what to use instead: a ring buffer built with modulo arithmetic, collections.deque, and the two-stack queue's amortised analysis.13 min
  19. Hash Tables in Python: How Dictionaries Get O(1) LookupsA dictionary lookup never searches: it computes an index from the key. How hash functions, buckets, collisions, chaining and resizing add up to average O(1).23 min
  20. Binary Search Trees in Python: Ordered Data That Stays FastThe invariant that makes search cost the height of the tree, insert and all three delete cases in Python, the four traversals, and how sorted input ruins it.19 min
  21. AVL Trees and Self-Balancing: Fixing the Binary Search Tree's Worst CaseHow an AVL tree stops a binary search tree degenerating into a linked list: balance factors, all four rotations, insert and delete with rebalancing, and the height proof.23 min
  22. Heaps and Priority Queues in Python: Always Knowing the Smallest ItemHow a binary heap keeps the smallest item one lookup away: the array trick, sift up and sift down from scratch, why building one is O(n), and all of heapq.25 min
  23. Tries in Python: The Data Structure Behind AutocompleteA trie stores words character by character, so insert, search and prefix queries cost the length of the key and never the number of keys. Built, measured and pruned in Python.23 min
  24. Union-Find (Disjoint Set) in Python: Tracking Connected Things FastUnion-find built twice: the naive version that degrades to O(n) per query, then union by size and path compression, and what the inverse Ackermann bound really promises.24 min
  25. Graphs in Python: Adjacency Lists, Matrices, and Which to UseVertices, edges and the vocabulary that goes with them, then adjacency lists, matrices and edge lists compared on memory, lookup cost and traversal speed, with a reusable Python Graph class.20 min
  26. Breadth-First Search (BFS) in Python: Shortest Paths, Level by LevelBreadth-first search traced by hand: a FIFO queue, level-by-level traversal, shortest paths in unweighted graphs, maze distances, and why you mark nodes on enqueue.19 min
  27. Depth-First Search (DFS) in Python: Going Deep Before Going WideDepth-first search from first principles: the recursive and iterative forms, why their visit orders differ, pre and post numbering, components, and cycle detection done right.20 min
  28. Dijkstra's Algorithm in Python: Shortest Paths With Weighted EdgesDijkstra's shortest paths worked through by hand: relaxation, the settled-is-final invariant, a heapq implementation using lazy deletion, and why one negative edge breaks it.20 min
  29. Bellman-Ford in Python: Shortest Paths When Edges Can Be NegativeWhy V-1 rounds of edge relaxation are exactly the right number, how one extra round proves a negative cycle exists, and how that becomes a currency arbitrage detector.16 min
  30. Floyd-Warshall in Python: Shortest Paths Between Every Pair of NodesHow three nested loops fill in the shortest route between every pair of vertices, why k has to be the outermost loop, and when running Dijkstra V times is the better call.14 min
  31. A* Search in Python: Dijkstra With a Sense of DirectionA* is Dijkstra with a heuristic added to the priority: f = g + h. What admissible and consistent mean, what an overestimate costs you, and node expansions counted against Dijkstra.25 min
  32. Minimum Spanning Trees in Python: Kruskal's and Prim's AlgorithmsKruskal's and Prim's algorithms in Python, with the cut property proved by an exchange argument, both traced on one seven-vertex graph, and the rule for choosing between them.25 min
  33. Topological Sort in Python: Ordering Tasks That Depend on Each OtherKahn's algorithm and the depth-first variant traced step by step on a real dependency graph, with the free cycle check and where the O(V + E) actually comes from.21 min
  34. Dynamic Programming Explained: Memoization, Tabulation, and How to Spot ItDynamic programming from the recursion tree up: overlapping subproblems, optimal substructure, memoization versus tabulation, rolling-row space cuts, and a five-step recipe.20 min
  35. The 0/1 Knapsack Problem in Python: The Classic DP Every Interview UsesThe 0/1 knapsack problem built up from brute force to the rolling array: the take-it-or-leave-it recurrence, the DP table, recovering the chosen items, and why the capacity loop counts down.21 min
  36. Longest Common Subsequence in Python: How diff Tools WorkThe dynamic programming table behind diff: subsequence versus substring, the two-case recurrence, a grid filled by hand, and the backtrack that recovers the subsequence itself.18 min
  37. Edit Distance (Levenshtein) in Python: Measuring How Different Two Strings AreHow Levenshtein distance is derived, filled into a table by hand, and read backwards to recover the actual insertions, deletions and substitutions, plus the Damerau variant.14 min
  38. The Coin Change Problem in Python: Where Greedy Fails and DP WinsTwo problems share one setup: the fewest coins that make an amount, and how many distinct ways make it. Why greedy breaks on coins 1, 3 and 4, and how a table fixes it.21 min
  39. Greedy Algorithms Explained: When Taking the Best Option Now Actually WorksGreedy algorithms take the best option now and never look back. When that is provably optimal, how the exchange argument proves it, and where the same rule silently fails.17 min
  40. Huffman Coding in Python: How Compression Actually CompressesHuffman coding built from scratch in Python: why prefix-free codes decode without separators, how the greedy heap merge finds the optimal tree, and what the header really costs.24 min
  41. Recursion and Backtracking in Python: Building the Mental ModelHow a recursive call actually works, drawn frame by frame on the call stack, then the choose-explore-un-choose pattern that turns recursion into a search over every arrangement.20 min
  42. The N-Queens Problem in Python: Backtracking at Its ClearestPlacing n queens with none attacking: how one queen per row collapses the search space, why row minus col and row plus col name the diagonals, and what the pruning really costs.20 min
  43. The KMP Algorithm in Python: String Search Without Ever Going BackwardsHow KMP searches a text without ever re-reading a character: building the LPS border table, the amortised proof of the O(n + m) bound, and when Python's str.find is the better answer.19 min
  44. Rabin-Karp in Python: Finding Substrings With Rolling HashesRabin-Karp turns every window of the text into one number: the rolling hash arithmetic derived step by step, why a hash match must always be verified, and where it beats KMP.20 min
  45. The Euclidean Algorithm in Python: The Oldest Algorithm Still in Daily UseEuclid's 2,300-year-old rule for the greatest common divisor: why gcd(a, b) equals gcd(b, a mod b), why it finishes in a handful of divisions, and what the extended version buys you.17 min
  46. Sieve of Eratosthenes in Python: Every Prime Under a Million, FastCross out multiples instead of testing numbers. Every prime below a million in 2.1 million writes and zero divisions, with the n log log n bound derived rather than asserted.20 min
  47. Fast Exponentiation in Python: Computing Huge Powers in log n StepsSquare-and-multiply turns n multiplications into about log2(n): the halving identity, recursive and iterative code, modular exponentiation, and Python's three-argument pow.15 min
  48. The Two Pointers Technique in Python: Turning O(n²) Into O(n)How two indices moving through one list replace a nested loop: the staircase proof behind the sorted pair sum, plus palindromes, in-place compaction, fast and slow, and 3-sum.20 min
  49. The Sliding Window Technique in Python: Subarray Problems Made LinearFixed and variable sliding windows in Python, with the amortised argument for why a nested while loop is still linear and the precondition that makes shrinking safe.23 min
  50. Prefix Sums in Python: Answering Range Queries in Constant TimeCompute cumulative totals once and any range sum becomes one subtraction: the n+1 sentinel that kills the off-by-one, 2D rectangles, and difference arrays.22 min
  51. Building an LRU Cache in Python: The Data Structure Behind Every CacheHow an LRU cache reaches O(1) for lookup, promotion and eviction by pairing a hash map with a doubly linked list, plus the standard library versions you should actually use.23 min