Explain why the work can be smaller
In this lesson4 sections
An array has ten thousand entries. Checking every pair means considering almost fifty million combinations. Before reaching for a faster language or a more elaborate data structure, ask what the input lets you rule out.
If the array is sorted, a pair that is already too large tells you something about many other pairs. If you are answering repeated range queries, most of the additions have already been done. If a graph edge has a fixed cost, the order in which you explore states can make a shortest-path result inevitable.
This course develops those observations into implementations you can explain. The aim is to recognize what information matters, preserve it in a useful form and avoid work that cannot change the answer.
Give the problem a precise shape
Before choosing an algorithm, identify the input, required output and constraints. “Find a pair” could mean returning whether a pair exists, its indices, every matching value pair or the number of matching index pairs. Duplicates behave differently in each version.
Write one small example and one boundary case. Ask whether you may reorder or mutate the input. Confirm whether a sequence is sorted, whether numbers may be negative and whether graph edges have direction. These details often decide which technique is valid.
Use a baseline as a reference
A straightforward solution gives you something to compare against. Enumerating all subsets is too expensive for a large input, but it can establish the expected answer on eight items. A slow pathfinder can verify a faster one on a tiny graph.
Then identify repeated work. Are you recomputing the same sum, exploring the same state or sorting after every insertion? Choose a representation that removes that cost while preserving the required distinctions.
For each improvement, complete this sentence: “After this step, the following fact remains true.” That fact is your invariant. For recursion, describe what a call returns. For dynamic programming, describe what one table entry means.
Count the operations your code performs
Big-O describes growth, not a measured runtime. A nested loop can be linear if the inner pointer only moves forward across the entire input. A short expression can be expensive if it copies a long slice or sorts a collection.
Distinguish auxiliary space from output space. Returning every subset requires storing or emitting exponentially many values regardless of how small the working recursion stack is. Python sorting and list copies also consume memory; “the pointers use constant space” does not describe the whole function if it first copies the array.
The examples use Python 3.10 or later. Complexity discussions generally treat numeric operations and hash lookups as constant-time operations on ordinary interview-sized values. Very large integers, adversarial hashing and language-specific allocation can require a more detailed model.
Read actively
For each lesson:
Solve the small example without code.
Name the state you need to keep.
Explain why a discarded choice cannot improve the result.
Implement the method, then challenge it with a boundary case.
Compare against a simple reference on small inputs.
The course starts with arrays and moves toward linked structures, trees, graphs, search and dynamic programming. You can use the section outline to revisit a technique, but spend time on the argument that makes it safe. That argument is what lets you adapt a familiar method when the problem changes.