An algorithm is a finite procedure for solving a class of problems. A useful explanation answers two questions: why does the procedure produce the right result, and what resources does it consume as the input grows?
Start with the invariant
Consider binary search over a sorted array. The algorithm keeps a range that is known to contain the target if the target exists. At each iteration it examines the middle element and discards the half that cannot contain the target. The invariant is what makes the answer correct; the halving is what makes the search logarithmic.
If there are n items, each comparison reduces the remaining range by roughly half, so the number of iterations grows like log₂(n). That statement describes asymptotic growth, not elapsed time. A binary search may perform fewer comparisons than a linear scan while making less predictable memory accesses. For a tiny array, the simpler scan can still be faster because contiguous data is easy for the processor to fetch and loop over.
Complexity describes a family of inputs
Big-O gives an upper growth bound up to constant factors. It helps compare how work scales, but it hides constants, data distribution, cache behavior, allocation, and the cost of an individual operation. Average-case claims need an explicit model of which inputs are likely. Amortized analysis answers a different question: across a sequence of operations, an occasional expensive operation may be spread over many cheap ones.
Choose a technique from the problem structure
Two pointers exploit order or a moving boundary. Sliding windows maintain a summary for a contiguous range. Divide and conquer splits a problem into smaller pieces and combines their results. Dynamic programming stores answers to overlapping subproblems. Graph algorithms make relationships explicit and then traverse or optimize paths through those edges.
For each technique, write down the state it maintains, the condition that changes it, and the work done per update. Then test edge cases such as empty input, duplicates, overflow boundaries, disconnected graphs, or adversarial ordering. These checks often reveal a missing invariant before a benchmark does.
From paper to machine
An algorithm's data structure controls its access pattern. A binary search jumps across an array; a linked traversal follows pointers; a breadth-first search uses a queue and touches neighbors by layer. Those patterns influence cache locality and branch prediction. Complexity gives a first map; a trace or profile explains a particular implementation's actual cost.
MIT's Introduction to Algorithms course is a useful companion for correctness and analysis.