Setting Up Recurrence Relations

Imagine you write a recursive function to sort a million numbers. You want to know — before running it — whether it will finish in a second or a year. Reading the code line by line will not tell you this directly. This is because the function calls itself, and the cost of each call depends on the cost of smaller calls. You need a way to compress an entire recursive computation into a single symbolic equation. This equation describes how the running time at size nn relates to the running time at smaller sizes. That equation is a recurrence relation. Setting it up correctly is the first and most consequential step in analyzing any recursive or divide-and-conquer algorithm. This translation step matters enormously in practice. Every time an engineer implements merge sort, a balanced binary search, the Karatsuba multiplication trick, or a recursive tree traversal, the running time is governed by a recurrence. Compilers, database query optimizers, and standard library implementations of sorting and searching all depend on this. Programmers, and automated cost models, must get this translation right. A single misread base case or an uncounted "combine" step can matter greatly. It can turn an O(nlog⁡n)O(n\log n) algorithm on paper into an accidental O(n2)O(n^2) algorithm in reality. This is a bug that no amount of testing on small inputs will reveal. It only shows up at scale. The big picture is this: recursive code has a natural correspondence to a mathematical recurrence. This recurrence is built from three ingredients. These are how many subproblems are made, how much smaller each subproblem is, and how much extra work is done outside the recursive calls. Once you can read code and mechanically produce T(n)T(n), you have converted an algorithmic question into a self-contained math problem. Downstream tools — the substitution method, recursion trees, the Master Theorem — can then solve it. This article teaches you to perform that translation reliably. It also teaches you to avoid the classic errors that corrupt it. These include missing or wrong base cases, undercounting combine work, and ignoring unequal subproblem sizes. They also include conflating the number of recursive calls with the number of leaves in the recursion. By the end, you will be able to look at almost any recursive algorithm. You will be able to write down its recurrence with confidence. It will then be ready to hand off to the solving techniques that follow.

Key takeaways
  • A recurrence T(n)T(n) expresses the running time of a recursive algorithm in terms of running times on smaller inputs.
  • Setting one up requires three pieces: the base case, the recursive term (number and size of subproblems), and the combine cost (non-recursive work per call).
  • The general divide-and-conquer template is T(n)=a⋅T(n/b)+f(n)T(n) = a \cdot T(n/b) + f(n), where aa = number of subproblems, n/bn/b = subproblem size, f(n)f(n) = combine cost.
  • Base cases must match the code's actual stopping condition, including boundary values like n=0n=0 or n=1n=1, or the recurrence silently misrepresents the algorithm.
  • Combine cost must include everything done outside recursive calls: loops, comparisons, merges, partitioning — not just the "obvious" step.
  • Getting T(n)T(n) right is a prerequisite for The Master Theorem, The Recursion-Tree Method, and The Substitution Method, which solve T(n)T(n) for a closed form.
The core insight

A recurrence relation is a faithful, line-by-line translation of recursive code's cost — one term per recursive call, one term for everything else, and an exact base case.

The idea

A recurrence relation for an algorithm's running time is an equation that defines T(n)T(n), the worst-case (or sometimes average-case) number of primitive operations on an input of size nn, in terms of TT evaluated at smaller arguments. It comes in two parts. The base case specifies T(n)T(n) directly for small, fixed values of nn — typically the input sizes at which the algorithm stops recursing and does O(1)O(1) work. The recursive case specifies T(n)T(n) for larger nn as a combination of calls to TT at smaller arguments, plus whatever extra work the algorithm performs outside those calls. The most common shape, arising from divide-and-conquer algorithms, is

T(n)=a⋅T(n/b)+f(n),T(n) = a \cdot T(n/b) + f(n),

read as: the algorithm divides a problem of size nn into aa subproblems, each of size roughly n/bn/b, solves each recursively, and then spends f(n)f(n) time combining the subproblem results (this f(n)f(n) also absorbs the cost of dividing the input in the first place). Here a≥1a \geq 1 is the number of recursive calls, b>1b > 1 is the factor by which the input shrinks, and f(n)f(n) is called the combine cost or driver function. Not every recursive algorithm fits this exact template — some have unequal subproblem sizes (e.g., quicksort's worst case), some subtract rather than divide (e.g., T(n)=T(n−1)+O(n)T(n) = T(n-1) + O(n)), and some make a variable number of calls depending on the input — but the discipline of identifying "how many calls, how much smaller, how much extra work" applies universally. The key terminology to keep straight: subproblem size is the argument passed to each recursive call, not the number of calls; branching factor (aa) is how many separate recursive invocations occur, not how many times the function shrinks the problem; and combine cost (f(n)f(n)) is measured per call at size nn, using the same operation-counting techniques used for iterative loops. Confusing any of these three produces a recurrence that looks plausible but describes a different algorithm than the one in front of you.

How it works

Setting up a recurrence is a mechanical, five-step reading of the code — you are not yet solving anything, only transcribing.

Step 1 — Identify the parameter of recursion. Decide what "nn" measures: array length, tree height, number of bits, distance between two indices, and so on. This must be a quantity that strictly decreases toward the base case along every recursive path.

Step 2 — Find every base case and its cost. Locate each `if`/guard that stops the recursion (e.g., `n <= 1`, `lo >= hi`, `node == null`). Count the operations performed there exactly as you would for straight-line code. This gives T(n)=Θ(1)T(n) = \Theta(1) (or whatever constant work applies) for those small nn.

Step 3 — Count the recursive calls and their argument sizes. For each call to the function itself, note the size of the argument passed. If there are aa calls each on a problem of size n/bn/b, that contributes a⋅T(n/b)a \cdot T(n/b). If sizes are unequal (say sizes n1,…,nkn_1, \dots, n_k), write ∑iT(ni)\sum_i T(n_i) instead of forcing them into a single T(n/b)T(n/b) term — do not paper over asymmetry.

Step 4 — Count everything else as f(n)f(n). Sum the cost of every operation that is not a recursive call: splitting/partitioning the input, scanning it, merging results, allocating auxiliary arrays, comparisons used to decide how to recurse. Use ordinary loop-counting to get f(n)f(n) as an explicit function of nn (e.g., Θ(n)\Theta(n) for a linear merge, Θ(1)\Theta(1) for computing a midpoint).

Step 5 — Assemble and simplify. Write

T(n)={Θ(1)n≤n0a T(n/b)+f(n)n>n0T(n) = \begin{cases} \Theta(1) & n \le n_0 \\ a\,T(n/b) + f(n) & n > n_0 \end{cases}

and simplify f(n)f(n) to its tightest simple bound (e.g., write Θ(n)\Theta(n) rather than "n−1n-1 comparisons plus 2 assignments"). At this point T(n)T(n) is fully specified; solving it is a separate task handled by later techniques. ``` FUNCTION SetUpRecurrence(code): // Step 1: identify the recursion parameter n n := the quantity that shrinks on every recursive path // Step 2: locate base case(s) for each stopping condition in code: record cost of that branch as a constant // T(n) = Θ(1) there // Step 3: locate recursive calls calls := list of (argument size, count) for each self-call if all calls have the same size n/b and there are a of them: recursive_term := a * T(n / b) else: recursive_term := sum of T(size_i) over each distinct call // Step 4: count non-recursive work f_n := cost of everything in code EXCLUDING the recursive calls (splitting, scanning, merging, bookkeeping) // analyze this exactly as an iterative loop would be analyzed // Step 5: assemble T(n) := base_case_cost if n small T(n) := recursive_term + f_n otherwise return T(n) ``` To see this in action, consider merge sort on an array of length nn: ``` MERGE-SORT(A, lo, hi): if lo >= hi: // base case: 0 or 1 element return mid = (lo + hi) / 2 MERGE-SORT(A, lo, mid) // recursive call #1, size ~n/2 MERGE-SORT(A, mid+1, hi) // recursive call #2, size ~n/2 MERGE(A, lo, mid, hi) // combine step, Θ(n) work ``` Applying the five steps: the recursion parameter is n=hi−lo+1n = hi - lo + 1; the base case is n≤1n \le 1 costing Θ(1)\Theta(1); there are a=2a = 2 recursive calls each on roughly n/2n/2 elements, so b=2b = 2; the non-recursive work is the `MERGE` call, which scans and merges two sorted halves in Θ(n)\Theta(n) time. Assembling gives

T(n)={Θ(1)n≤12 T(n/2)+Θ(n)n>1.T(n) = \begin{cases} \Theta(1) & n \le 1 \\ 2\,T(n/2) + \Theta(n) & n > 1. \end{cases}

This is the recurrence that The Master Theorem later solves to Θ(nlog⁡n)\Theta(n \log n) — but notice that arriving at 2T(n/2)+Θ(n)2T(n/2) + \Theta(n) required nothing beyond careful reading; no solving happened yet.

Figure
Anatomy of a divide-and-conquer recurrence, read directly off the code
MERGE-SORT(A, lo, hi) → T(n) if lo ≥ hi: return mid = (lo+hi)/2 MERGE-SORT(A, lo, mid) MERGE-SORT(A, mid+1, hi) MERGE(A, lo, mid, hi) size n = hi - lo + 1 base: n ≤ 1 base case → T(n) = Θ(1), n ≤ 1 2 calls, size n/2 → a·T(n/b) = 2T(n/2) merge scan → f(n) = Θ(n) T(n) = 2 T(n/2) + Θ(n), T(n) = Θ(1) for n ≤ 1 a = 2 subproblems, b = 2 (halved), f(n) = Θ(n) combine cost

Complexity & correctness

Setting up the recurrence is not itself an algorithm with a running time — it is an act of modeling. So "complexity" here means: how do we know the recurrence we wrote is a faithful and tight description of the algorithm's actual cost? Correctness of the setup rests on three checks, each corresponding to a place errors creep in.

Check 1 — base case fidelity. The base case in T(n)T(n) must trigger for exactly the same inputs as the base case in the code, and its stated cost must match the actual work done there (not assumed to be free). If code checks `n <= 1` but the recurrence assumes `n == 0`, sizes like n=1n=1 get silently routed through the recursive branch of TT, which may not even be defined for such a small argument (e.g., n/2n/2 under integer division could loop or misbehave). This is why Correctness of Recursive Algorithms by Induction and recurrence setup are companions: the induction proof of correctness and the recurrence for cost must use identical base cases, since both rely on the same well-founded decrease in nn.

Check 2 — recursive term fidelity. The number of calls aa and the subproblem size n/bn/b must be read directly from the call sites, not guessed from the algorithm's name or reputation. A common trap is writing T(n/2)T(n/2) when the code actually recurses on sizes n/2n/2 and n/2−1n/2 - 1 (fine, both are Θ(n)\Theta(n)-close and usually absorbed), versus code that recurses on wildly unequal pieces, such as quicksort's worst case (00 and n−1n-1), which must be written as T(0)+T(n−1)T(0) + T(n-1), not forced into a balanced template.

Check 3 — combine-cost completeness. f(n)f(n) must include all non-recursive work: splitting the array, computing a pivot, allocating temporary storage, and any post-processing after the recursive calls return — not just the step that "feels" like the combine phase. Undercounting here is the single most common source of an incorrect asymptotic bound; for instance, forgetting that array slicing (rather than passing indices) costs Θ(n)\Theta(n) per call silently upgrades a Θ(nlog⁡n)\Theta(n\log n) recurrence to Θ(n2)\Theta(n^2) in practice, because f(n)f(n) was mis-recorded as Θ(1)\Theta(1). Given these checks, the recurrence is a correct model in the following precise sense: if Tcode(n)T_{\text{code}}(n) denotes the true number of primitive steps the algorithm performs on an input of size nn, and T(n)T(n) is the recurrence built by the five-step procedure, then Tcode(n)=T(n)T_{\text{code}}(n) = T(n) exactly (up to the constants hidden in Θ\Theta-notation for the base case and f(n)f(n)), by structural induction on nn: the base case matches by Check 1, and for the inductive step, the code's total work at size nn is by definition the sum of the work done in the recursive calls — which by the inductive hypothesis is ∑iTcode(ni)=∑iT(ni)\sum_i T_{\text{code}}(n_i) = \sum_i T(n_i) — plus the non-recursive work f(n)f(n) by Check 3, which is exactly the recursive case of T(n)T(n). The honest tradeoff is that this setup step produces T(n)T(n) but does not tell you its closed form or growth rate; that is deferred to The Substitution Method, The Recursion-Tree Method, and The Master Theorem. A perfectly set-up recurrence can still be hard to solve (e.g., T(n)=T(n−1)+T(n−2)+O(1)T(n) = T(n-1) + T(n-2) + O(1), the Fibonacci recurrence, grows exponentially despite looking similar in shape to easier ones), so setup and solving are genuinely separate skills, and mistakes in either stage compound.

Worked example

Consider a recursive algorithm for finding the maximum of an array by splitting it into three (not two) unequal parts, a variant deliberately chosen to avoid the too-clean symmetric case: ``` MAX3(A, lo, hi): n = hi - lo + 1 if n == 1: return A[lo] if n == 2: return max(A[lo], A[hi]) third = n / 3 m1 = lo + third m2 = lo + 2third x = MAX3(A, lo, m1) // size ~ n/3 y = MAX3(A, m1+1, m2) // size ~ n/3 z = MAX3(A, m2+1, hi) // size ~ n/3 (or n - 2(n/3), possibly larger by rounding) return max(x, max(y, z)) // Θ(1) combine ``` Walk through the five steps explicitly.

StepWhat we read from the codeContribution
1. Recursion parametern=hi−lo+1n = hi - lo + 1defines input size
2. Base cases`n == 1` returns directly; `n == 2` does one comparisonT(1)=Θ(1)T(1) = \Theta(1), T(2)=Θ(1)T(2) = \Theta(1)
3. Recursive calls3 calls, sizes n/3,n/3,n/3, n/3, and n−2⌊n/3⌋n - 2\lfloor n/3 \rfloor (roughly n/3n/3 each, but not exactly equal due to integer division)≈3 T(n/3)\approx 3\,T(n/3)
4. Non-recursive worktwo comparisons in the final `max(x, max(y,z))` line, plus O(1)O(1) index arithmeticf(n)=Θ(1)f(n) = \Theta(1)
5. Assemblecombine piecesT(n)=3T(n/3)+Θ(1)T(n) = 3T(n/3) + \Theta(1), with T(1)=T(2)=Θ(1)T(1)=T(2)=\Theta(1)

A subtlety worth pausing on: the three subproblem sizes are not identical (integer division makes the last chunk possibly one or two elements larger), but since they differ by at most O(1)O(1), it is standard and justified practice to write 3T(n/3)3T(n/3) rather than T(⌈n/3⌉)+T(⌈n/3⌉)+T(n−2⌈n/3⌉)T(\lceil n/3\rceil) + T(\lceil n/3 \rceil) + T(n - 2\lceil n/3\rceil) — the discrepancy only affects lower-order terms, which the solving techniques (The Master Theorem in particular) are explicitly built to absorb. This is a judgment call that setup requires: know when rounding is safe to elide (bounded, O(1)O(1)-additive discrepancies) versus when it changes the asymptotics (as with unbalanced splits like 1/41/4 vs. 3/43/4, which still needs the shape a T(n/b)a\,T(n/b) but with different constants per branch if sizes are genuinely proportionally unequal). Now trace the recurrence numerically for n=9n = 9 to double check it against direct code execution, assuming f(n)=1f(n) = 1 unit exactly and T(1)=T(2)=1T(1) = T(2) = 1 unit:

T(9)=3 T(3)+1,T(3)=3 T(1)+1=3(1)+1=4.T(9) = 3\,T(3) + 1, \qquad T(3) = 3\,T(1) + 1 = 3(1) + 1 = 4.
T(9)=3(4)+1=13.T(9) = 3(4) + 1 = 13.

Cross-checking against the code: at n=9n=9, three recursive calls each on size 3 (so 9 total leaf-level base cases at n=1n=1, each costing 1, giving 99 units), plus the internal combine steps at each of the 4 internal invocation levels shown above (the top call plus 3 middle calls = 4 combine steps of 1 unit each) gives 9+4=139 + 4 = 13 — matching exactly. This numeric cross-check is a good habit: after setting up T(n)T(n), unroll it two or three levels by hand and compare against a direct trace of the code on a small concrete input, as a sanity check before handing the recurrence off to a solving method.

Common pitfalls & traps

PitfallWhy it happensConsequence
Base case mismatch (code checks `n<=1`, recurrence assumes `n==0`)Copying a "textbook" base case instead of reading the actual guardT(n)T(n) undefined or wrong at small nn; can propagate errors upward
Forgetting to count the "split" stepFocusing only on the recursive calls and the final merge, ignoring e.g. an O(n)O(n) partition before recursingf(n)f(n) silently missing a term, changing the growth rate (e.g. quicksort's partition is easy to undercount)
Treating unequal subproblems as if balancedWanting the clean aT(n/b)aT(n/b) templateMasks true worst-case behavior, e.g. writing 2T(n/2)2T(n/2) for quicksort instead of T(k)+T(n−1−k)T(k) + T(n-1-k)
Counting number of leaves instead of number of calls at the top levelConfusing "how many times T(1)T(1) is eventually reached" with "aa" in aT(n/b)+f(n)aT(n/b)+f(n)Produces a recurrence with the wrong branching factor, wrong solved growth rate
Ignoring cost of non-primitive operations passed to recursive calls (e.g. array slicing/copying vs. passing indices)Treating a language's slice syntax as "free"Turns Θ(nlog⁡n)\Theta(n\log n) into Θ(n2)\Theta(n^2) purely from unaccounted copying
Writing f(n)f(n) as a description instead of a functionLeaving it as "the merge step" without quantifyingDownstream solving methods cannot proceed without an explicit f(n)f(n)

Pioneers & history

The systematic use of recurrence relations to analyze algorithms emerged alongside the formalization of divide-and-conquer as a design paradigm, though the mathematics of recurrences long predates computing.

Origins · 1 minJohn von Neumann and how this was discovered
JV
John von Neumann
1903–1957 · Early divide-and-conquer analysis

Von Neumann is credited with one of the earliest divide-and-conquer sorting algorithms, merge sort, described in a 1945 report, which implicitly required reasoning about how the cost of sorting halves combines into the cost of sorting the whole — an early instance of exactly the recurrence-setup discipline this article formalizes, though the explicit T(n)=aT(n/b)+f(n)T(n)=aT(n/b)+f(n) notation came later with the systematic study of algorithm analysis.

History
1945
— von Neumann describes merge sort, an early divide-and-conquer algorithm requiring recurrence-style cost reasoning.
1962
— Tony Hoare publishes quicksort, whose recurrence T(n)=T(k)+T(n−1−k)+Θ(n)T(n) = T(k) + T(n-1-k) + \Theta(n) becomes a standard teaching example of unequal-split recurrences.
1968
— Donald Knuth's The Art of Computer Programming systematizes recurrence relations as the standard tool for analyzing recursive algorithms.
1974
— Aho, Hopcroft, and Ullman's The Design and Analysis of Computer Algorithms popularizes the aT(n/b)+f(n)aT(n/b)+f(n) template for divide-and-conquer.
1990
— Cormen, Leiserson, and Rivest's Introduction to Algorithms (CLRS) codifies recurrence setup, substitution, recursion trees, and the Master Theorem as a unified pedagogical sequence.

Where this matters

Recurrence setup is the analytical backbone behind every divide-and-conquer library routine in practice: standard sorting implementations (merge sort variants used in Python's Timsort, Java's `Arrays.sort` for objects, and C++'s `std::stable_sort`) are all designed and tuned by first writing down and solving their recurrences, then choosing thresholds (like switching to insertion sort below some small nn) informed by the base-case behavior of T(n)T(n). Fast multiplication algorithms — Karatsuba's algorithm and the Fast Fourier Transform-based methods used in cryptographic libraries and big-integer arithmetic (as in Python's `int`, or GMP) — are justified by recurrences like T(n)=3T(n/2)+Θ(n)T(n) = 3T(n/2) + \Theta(n), whose solved form directly explains why they beat naive Θ(n2)\Theta(n^2) multiplication. Computational geometry algorithms (closest-pair, convex hull) and recursive tree/graph algorithms (binary search tree operations, recursive descent parsers used in every compiler front-end) are all analyzed the same way. In distributed and parallel computing, recurrences of the form T(n)=T(n/2)+O(log⁡n)T(n) = T(n/2) + O(\log n) describe communication rounds in parallel algorithms, guiding real infrastructure decisions such as how MapReduce-style reduction trees or parallel prefix-sum implementations are structured. Anywhere a system recurses — compilers, database query planners choosing between recursive join strategies, or recursive descent in regular-expression engines — setting up the recurrence correctly is the first analytical step engineers take before trusting a recursive implementation at scale.

Ask the tutor: How do I set up the recurrence for an algorithm that recurses a variable number of times depending on the input, rather than a fixed count like 2 or 3?

What is the difference between setting up a recurrence and solving it?

Setting up a recurrence means translating recursive code into a symbolic equation T(n)T(n) that faithfully describes its cost — identifying the base case, the recursive calls, and the combine cost. Solving the recurrence means finding a closed-form or asymptotic bound (like Θ(nlog⁡n)\Theta(n\log n)) for that T(n)T(n), using techniques such as The Substitution Method, The Recursion-Tree Method, or The Master Theorem. These are deliberately separate skills: you can set up a correct recurrence and still find it hard to solve (e.g., recurrences with subtractive or unequal splits often resist the Master Theorem and need substitution or trees instead). Conversely, a beautifully solvable recurrence is worthless as an analysis if it was set up incorrectly and does not actually match the code's behavior. Mastering setup first, independent of solving, avoids conflating "I don't know how to solve this" with "I don't know what the recurrence is."

How do I handle recursive calls that produce unequal-sized subproblems?

Do not force unequal subproblems into a balanced a T(n/b)a\,T(n/b) template just because it looks cleaner. If the code recurses on pieces of size n1,n2,…n_1, n_2, \dots, write the recurrence as the literal sum T(n1)+T(n2)+⋯+f(n)T(n_1) + T(n_2) + \cdots + f(n). For example, quicksort with pivot producing a split of size kk and n−1−kn-1-k gives T(n)=T(k)+T(n−1−k)+Θ(n)T(n) = T(k) + T(n-1-k) + \Theta(n). For worst-case analysis, you then consider the value of kk (over all possible pivot choices) that maximizes T(n)T(n), which for quicksort is k=0k=0 or k=n−1k=n-1, giving T(n)=T(n−1)+Θ(n)T(n) = T(n-1) + \Theta(n). Forcing a false symmetry hides exactly the worst-case behavior you are usually trying to capture, so unequal splits should be preserved explicitly until you decide, during solving, whether to analyze best, worst, or average case.

Does the combine cost f(n) always have to be measured with big-Theta, or can it be a range?

It is often most useful to state f(n)f(n) as a tight Θ\Theta-bound when you know the exact work (e.g., a linear merge is Θ(n)\Theta(n)), but in worst-case analysis you sometimes only have an upper bound, in which case f(n)=O(n)f(n) = O(n) is appropriate and the resulting T(n)T(n) is itself only an upper bound, i.e., O(⋅)O(\cdot) rather than Θ(⋅)\Theta(\cdot). What matters most is honesty: if the code's non-recursive work varies with input (e.g., a partition step whose comparisons depend on data), state the bound you can actually justify — often the worst case over all possible executions of that step — rather than assuming the friendliest case. Mixing best-case combine costs into a worst-case recurrence is a subtle but common error that produces an overly optimistic T(n)T(n).

What if the recursive parameter isn't the array length — like a linked list, a tree, or a graph?

The five-step procedure still applies; only the choice of "nn" changes. For a binary tree traversal, nn is usually the number of nodes, the base case is an empty subtree (`node == null`), and the recursive calls are on the left and right subtrees, whose sizes sum to n−1n-1 (since sizes are generally unequal unless the tree is balanced), giving T(n)=T(nL)+T(nR)+Θ(1)T(n) = T(n_L) + T(n_R) + \Theta(1) where nL+nR=n−1n_L + n_R = n - 1. For recursion on a linked list, nn is the remaining list length, and calls typically reduce nn by exactly 1 each time, giving recurrences like T(n)=T(n−1)+Θ(1)T(n) = T(n-1) + \Theta(1). The discipline is identical: pick a decreasing, well-defined size measure, find the exact base case, and count calls and non-recursive work precisely — the structure being recursed over doesn't change the method, only the bookkeeping.

Why does merge sort's recurrence use 2T(n/2) and not something like T(n/2) + T(n/2) written separately?

They are mathematically identical when the two subproblems are the same size, and 2T(n/2)2T(n/2) is simply the standard shorthand once you've confirmed both calls really do operate on equal (or equal up to O(1)O(1)) sized pieces — as merge sort's two halves do. The shorthand a T(n/b)a\,T(n/b) is only valid, however, once you have explicitly checked that all aa subproblems truly share the same size n/bn/b (or differ negligibly); if they do not, as in quicksort, you must fall back to writing each term separately, e.g. T(k)+T(n−1−k)T(k) + T(n-1-k), rather than defaulting to the compact notation out of habit. The compactness is a convenience earned by verifying symmetry, not a default assumption.

Can a recurrence have more than one recursive term with different subproblem sizes, like T(n) = T(n/2) + T(n/4) + Θ(n)?

Yes, and such recurrences arise naturally from algorithms that split input unevenly by design (not just by bad luck, as in quicksort), for instance certain divide-and-conquer geometric or dynamic-programming-adjacent algorithms that recurse on differently sized partitions on purpose. These recurrences require more general solving tools than a single-term Master Theorem application — often the The Recursion-Tree Method handles them well because it lets you sum costs level by level even when branches have different sizes and thus different depths to their base cases. The setup procedure is unchanged: read off each distinct recursive call, record its exact argument size, and do not try to average or combine terms with different bb values into a single a T(n/b)a\,T(n/b) expression.

How does recurrence setup relate to proving an algorithm correct by induction?

They share the same backbone — a well-founded decrease in a size parameter nn down to an explicit base case — but answer different questions. Correctness of Recursive Algorithms by Induction uses this structure to prove the algorithm produces the right answer, with the inductive hypothesis being "the recursive calls return correct results." Recurrence setup uses the identical structure to track how much time is spent, with the inductive step being "the cost at nn is the sum of recursive costs plus f(n)f(n)." Because both proofs rely on the same base case and the same decomposition into subproblems, any inconsistency between the base cases used for correctness and for cost is a red flag: it usually means one of the two analyses is using a stale or incorrect understanding of when the recursion actually stops.

Before this

Where this leads