Study overview
Complete digital class notes · term tests + semester final · offline

Algorithm theory

Complete, readable class material: definitions, derivations, algorithms, worked examples, tables and corrected explanations are written directly in the lessons. Open the original scans only when you want to compare the handwriting or check the source.

How the sources are handled: overlapping notebooks are combined into one complete explanation. Demonstrable mistakes are corrected in the main text and flagged nearby; ambiguous handwriting is identified rather than silently guessed. All 194 embedded reference images, 21 complete C++ listings and 254 past-question cards remain available. Curriculum-only supplements are labeled separately from taught material.

Term tests + semester final · complete academic reference

How to read these digital class notes

  1. Learn from the main text. Start with algorithm foundations, then follow the classroom topics. TT1 page numbers match the original notebook; neighbouring pages form a continuous explanation.
  2. Check a source only when needed. Original handwriting, slides and alternate copies are in closed reference drawers below each lesson. Corrections are visible in the main text.
  3. Practice after understanding. Use the full C++ library, term-test route, final-exam checklist and topic-linked past questions. Write a trace, derivation or proof before checking the answer.
  4. Distinguish taught material from supplements. The curriculum map identifies additional syllabus topics; their inclusion is not a claim that they appeared in a supplied lecture.

Everything is embedded for offline use. “Digital class notes only” prints the full explanations without scans, code-library duplication or revision summaries.

Teacher-provided foundation

Introductory slides

Teacher slide content, with notation corrections clearly separated.

Teacher-provided slide deck

Algorithm foundations & asymptotic analysis

Teacher slides: algorithm definition, characteristics, algorithm families, performance, time/space complexity, and asymptotic notation.

Teacher material17 slides

What an algorithm means

An algorithm is a finite, well-defined sequence of instructions that solves a specified problem. It describes the complete method, not a particular programming language. A program implements that method. An input instance supplies the data; the output must satisfy the problem specification for every valid instance.

For example, “find the largest number” is a problem, not a complete algorithm. “Store the first element as the current maximum; inspect each remaining element; replace the maximum when a larger value is found; return the maximum” specifies an executable method for a nonempty array.

Characteristics in the introductory lecture

  1. Finiteness: execution takes a finite number of steps for every valid input.
  2. Input: zero or more clearly specified input values, with a defined valid range.
  3. Output: at least one result related to the input and the required task.
  4. Definiteness: every instruction has one precise meaning.
  5. Correctness: the result solves the stated problem, not merely a few examples.
  6. Unambiguous control: the order of execution, conditions, and repetition are explicit.
  7. Termination: the procedure stops by itself rather than looping forever.

The handwritten note additionally calls each step effective: it must be basic enough to carry out in finite time. “Guess the correct answer” is not an effective computational instruction unless the allowed operation and its cost are defined.

Algorithm-design families

FamilyComplete ideaLecture examples
Brute forceSystematically inspect candidates, keeping valid or best solutions. It needs no clever shortcut but may repeat much work.Linear search; naïve string matching
GreedyCommit to a locally best feasible choice. A proof is needed that a global optimum is not lost.Dijkstra; Prim; Kruskal
Divide and conquerSplit into smaller instances; solve recursively; combine their results.Merge sort; quicksort; binary search
Dynamic programmingDefine repeated subproblems, solve each state once, and reuse its stored result.Fibonacci; 0–1 knapsack; Floyd–Warshall
BacktrackingBuild a candidate one decision at a time; undo a decision when the partial candidate cannot lead to a valid solution.N-Queens; Sudoku; Hamiltonian path
Branch and boundExplore a solution tree while using optimistic bounds to discard branches that cannot improve the best complete solution.TSP; 0–1 knapsack
RandomizedMake random choices during execution and analyze the resulting correctness or running-time guarantees.Randomized quicksort; Monte Carlo methods
ApproximationReturn a feasible near-optimal answer with a proved quality guarantee, usually in polynomial time.Vertex cover; suitable TSP variants

Performance, efficiency, and the input-size model

Different correct algorithms can solve the same problem. Their suitability depends on input size, input distribution, memory available, and the required result. The two main resources in the lecture are time and space. A faster method may need more memory; a memory-saving method may repeat calculations. “Efficient” therefore means an appropriate trade-off, not that one algorithm wins in every situation.

Time complexity counts elementary operations under a stated cost model rather than seconds on one computer. Examples are comparisons, additions, assignments, and array accesses. Let n denote array length or string length; for a graph, specify both V and E. Space complexity counts simultaneously live storage. Auxiliary space excludes the input, while total space includes it; state which convention is used. Recursive calls also consume stack space.

A constant-time arithmetic assumption is suitable for fixed-size machine integers. When a value may have arbitrarily many bits, arithmetic is not generally constant time. This matters for large-number algorithms and explains why a bound such as O(nW) for knapsack is called pseudo-polynomial.

Best, worst, and average case are different from asymptotic bounds

For all inputs of size n, the best-case running time is the minimum, the worst-case is the maximum, and the average-case is an expectation under a specified input distribution. In linear search, finding the key first costs Θ(1); finding it last or not finding it costs Θ(n). A uniformly distributed successful position also gives Θ(n) expected comparisons.

Big-O, Ω, and Θ instead compare functions as n grows. They can describe a best-case, worst-case, average-case, or other cost function:

NotationFormal condition for sufficiently large nMeaning of the lecture graph
f(n) ∈ O(g(n))There exist c>0 and n₀ such that 0 ≤ f(n) ≤ c·g(n) for every n≥n₀.After n₀, f stays below a constant multiple of g: an upper bound.
f(n) ∈ Ω(g(n))There exist c>0 and n₀ such that 0 ≤ c·g(n) ≤ f(n) for every n≥n₀.After n₀, f stays above a constant multiple of g: a lower bound.
f(n) ∈ Θ(g(n))There exist c₁,c₂>0 and n₀ such that 0 ≤ c₁g(n) ≤ f(n) ≤ c₂g(n) for every n≥n₀.The two scaled curves sandwich f: a tight asymptotic bound.
Correction / clarification. The slide captions equate O with “worst”, Ω with “best”, and Θ with “average”. Those equations are not definitions. Θ means a tight bound, not average time. For example, merge sort has worst-case Θ(n log n).

Example: f(n)=3n²+2n+7. For n≥1, 3n²≤f(n)≤12n², so f(n)=Θ(n²). It is also O(n³), but that upper bound is not tight. Constant factors and lower-order terms disappear from the growth class, not from the real program's measured cost.

Reading the growth-rate charts

For sufficiently large n, the typical increasing order is 1 < log n < √n < n < n log n < n² < n³ < 2ⁿ < n!. All logarithms with fixed bases greater than 1 differ by a constant factor. Polynomial time means O(nᵏ) for a fixed constant k; nⁿ is not polynomial time. The charts describe growth, not exact timing or a guarantee that every quadratic program is unusable.

Data-structure operation table, with the missing assumptions stated

The lecture includes an overview table of common structures. An operation must be named before a cost is meaningful; “an array is O(n)” is incomplete. Here is the readable version with conditional entries clarified. Space is O(n) unless a row says otherwise.

StructureAccess / searchInsertion / deletion and qualifications
ArrayIndex access Θ(1); unsorted search Θ(n) worst.Arbitrary insertion/deletion Θ(n) worst because elements shift. Appending to a dynamic array is amortized Θ(1), not arbitrary insertion.
Stack / queueTop/front access Θ(1); search Θ(n).Push/pop or enqueue/dequeue Θ(1) with a suitable linked structure or circular buffer; resizing arrays give amortized bounds.
Singly / doubly linked listIndex access and unsorted search Θ(n).Θ(1) given the necessary node links. Singly linked deletion needs the predecessor; finding it can take Θ(n).
Skip listExpected O(log n) search; O(n) worst.Expected O(log n) updates; O(n) worst. Expected O(n) storage; a height-capped representation may allow O(n log n) worst-case pointer storage.
Hash tableExpected O(1) lookup under suitable hashing/load assumptions; O(n) worst.Expected O(1) insert/delete; collisions or rehashing can make a single operation O(n).
Ordinary binary search treeO(h) search, where h is tree height.O(h) updates; expected O(log n) under suitable random shape assumptions, but h can be n.
AVL / red-black treeO(log n) search.O(log n) updates because balancing keeps height logarithmic.
B-treeO(log n) comparisons in the usual abstract model; O(log_B n) block levels.Balanced multiway search/update; block capacity B matters in external-memory analysis.
Splay treeO(log n) amortized over a sequence; one operation may take O(n).The same amortized qualification applies to updates. The slide’s logarithmic “worst” entry should not be read as a per-operation guarantee.
Cartesian treePreserves sequence order and a heap property; no universal logarithmic dictionary-search bound.A treap adds randomized priorities and a search-key ordering, giving expected O(log n) operations. Do not transfer this bound to every Cartesian tree.
k-d treeBalanced-tree exact-point lookup can be logarithmic; performance depends on dimension, balance, and query type.Worst-case searches can be linear. Range and nearest-neighbour queries do not share one universal O(log n) bound.
Binary heapRoot min/max Θ(1); finding an arbitrary key Θ(n).Insert/extract root O(log n); bottom-up build Θ(n).

Search and sorting comparison from the lecture

AlgorithmBestAverage / expectedWorst
Linear searchΘ(1)Θ(n) under uniformly distributed successful positionsΘ(n)
Binary search, sorted random-access arrayΘ(1)Θ(log n) under usual successful-search assumptionsΘ(log n)
Bubble sort with early-stop flagΘ(n)Θ(n²)Θ(n²)
Selection sortΘ(n²)Θ(n²)Θ(n²)
Insertion sortΘ(n)Θ(n²)Θ(n²)
Standard merge sortΘ(n log n)Θ(n log n)Θ(n log n)
Quicksort, distinct keys / balanced or random-pivot analysisΘ(n log n)Expected Θ(n log n)Θ(n²)
HeapsortO(n log n)Θ(n log n) under usual distinct-key analysisΘ(n log n)

These tables are orientation material, not evidence that every listed structure received a full lecture. The taught algorithm chapters below give the actual procedures, conditions, examples, and derivations.

Interpreting the performance claims and figures

Correction / clarification. Slides 6–8 emphasize performance, describe hours of running time as “of no use”, and describe gigabytes of memory as undesirable. These are motivation examples, not universal thresholds. A valid algorithm may take hours or substantial memory and still meet its application’s needs. Correctness, feasibility, input conditions and the available resource budget must also be checked; performance is not literally the only selection criterion.

In the asymptotic diagrams, the horizontal axis is input size n and the vertical axis is the cost function. The vertical marker n₀ begins the range in which the chosen inequality must hold for every n. Crossings before that threshold do not invalidate an asymptotic bound. In the Θ diagram, both a positive lower multiplier c₁ and an upper multiplier c₂ are required; an arbitrary curve “between best and worst” is not its definition.

The two growth charts use computation count or completion time on the vertical axis. Their labels from slowest to fastest eventual growth include 1, log n, √n, n, n log n, n², n³, 2ⁿ, n! and nⁿ. In particular, n! grows more slowly than nⁿ. The slide’s label “polynomial” beside n³ names one example: n and n² are polynomial too. Color labels such as “excellent” and “horrible” are informal illustrations, not input-independent acceptance rules.

Operation-by-operation reading of the data-structure chart

“Access” is ambiguous across structures. For an array it means indexing; for a linked list, stack or queue in the slide it means reaching an arbitrary position, not just the permitted top/front. The following table supplies the operation distinctions needed to read the chart without its image. Bounds are upper bounds; expected and amortized qualifiers are part of the answer.

StructureArbitrary-position accessSearch by value/keyInsert / delete
ArrayO(1)O(n) unsortedO(n) / O(n) with shifting
StackO(n) to reach an arbitrary entry; top O(1)O(n)Push/pop O(1) with linked storage; amortized O(1) with a resizing array
QueueO(n) to reach an arbitrary entry; front O(1)O(n)Enqueue/dequeue O(1) with linked storage or a suitable circular buffer; resizing may be amortized
Singly linked listO(n)O(n)O(1) when needed predecessor links are known; otherwise locating them costs O(n)
Doubly linked listO(n)O(n)O(1) given the relevant node; finding a node by value is O(n)
Skip listRank access needs extra span information; walking level zero is O(n)Expected O(log n), worst O(n)Expected O(log n), worst O(n), under the usual height cap
Hash tableNo general positional-access operationExpected O(1), worst O(n)Expected/amortized O(1), worst O(n), with suitable hashing and load assumptions
Ordinary BSTKey-directed access O(h); rank needs subtree sizesO(h): expected O(log n) for suitable random shape, worst O(n)O(h) / O(h)
Cartesian treeNo general indexed-access guaranteeO(n) without an additional search-key organizationDepends on representation and permitted update; no general O(log n) guarantee
B-treeKey access/search over O(log_B n) block levelsO(log n) key comparisons with suitable within-node searchLogarithmic height; split/merge work depends on block capacity B
Red-black treeKey-directed O(log n); rank needs augmentationO(log n)O(log n) / O(log n)
Splay treeRank access requires augmentationAmortized O(log n), individual worst O(n)Amortized O(log n), individual worst O(n)
AVL treeKey-directed O(log n); rank needs augmentationO(log n)O(log n) / O(log n)
k-d treeNo general array-index access; point lookup follows spatial splitsExact-point lookup O(h), with h logarithmic only if balanced; range/nearest queries differDepends on balance maintenance and the update algorithm; may be linear
Correction / clarification. The slide’s stack/queue access O(n) does not contradict top/front O(1). Its Cartesian-tree expected logarithmic entries need treap-like search-key/random-priority assumptions, its splay-tree logarithmic entries are amortized rather than individual worst-case bounds, and a k-d tree cannot be assigned one bound independent of query and update type. Similarly, the next slide’s “heap: best O(1), average O(log n), worst O(n)” mixes operations: inspecting the root is O(1), extracting it is O(log n), and searching an arbitrary value is O(n).

STL self-study handout — supporting implementation reference

The supplied STL Self_Study.docx describes the Standard Template Library as reusable C++ classes and functions for handling data and implementing algorithms. It asks students to understand these facilities because they appear in the class’s lab programs, while explicitly placing detailed teaching of the data-structure shortcuts outside the theory syllabus. This is a supporting handout, not an additional handwritten theory lecture.

Read the class programs in terms of their roles: a container stores elements, an iterator identifies a position/range, and a library algorithm operates on that range. For example, sorting a vector of item records supplies the greedy order; a priority queue supplies repeated minimum/maximum selection according to its comparator. Using a library facility does not remove the need to explain the course algorithm or include that facility’s operation cost in the analysis.

The handout supplies these optional external references (an internet connection is needed only to open them; the course explanations and full class code in this reader remain offline):

↗Open the original teacher slide deck17 source pages
Introductory lecturepage 1 of 17
Introductory lecturepage 2 of 17
Introductory lecturepage 3 of 17
Introductory lecturepage 4 of 17
Introductory lecturepage 5 of 17
Introductory lecturepage 6 of 17
Introductory lecturepage 7 of 17
Introductory lecturepage 8 of 17
Introductory lecturepage 9 of 17
Introductory lecturepage 10 of 17
Introductory lecturepage 11 of 17
Introductory lecturepage 12 of 17
Introductory lecturepage 13 of 17
Introductory lecturepage 14 of 17
Introductory lecturepage 15 of 17
Introductory lecturepage 16 of 17
Introductory lecturepage 17 of 17
Teacher handwritten core

TT-1 — page-by-page reader

All 44 pages retained. Historical “extra/supporting/excluded” TT labels do not remove anything.

Page 01Analysis foundations

Algorithm: definition and conditions

Teacher taughtHandwritten

Definition and five conditions

An algorithm is a step-by-step procedure for solving a problem: a well-defined sequence of instructions that, when followed on valid input, produces the required result. The notebook lists five conditions.

ConditionWhat must be trueA useful distinction
FinitenessOnly finitely many steps are executed on each valid input.An input-dependent loop can be finite even though its length varies.
DefinitenessEvery instruction and branch is precise and unambiguous.“Repeat until suitable” is incomplete unless “suitable” is defined.
InputThe algorithm accepts zero or more specified inputs.The valid-input domain belongs to the problem statement.
OutputAt least one specified result is produced.A decision algorithm may simply return true or false.
EffectivenessEach step can actually be performed in finite time.This is not the same as being fast overall.

Correctness and termination must both hold. A program that always stops but gives a wrong answer is not a correct algorithm for the problem. A procedure that would eventually print a correct answer but may loop forever also fails the requirement.

For a maximum-finding scan, the loop invariant is: after processing the first k elements, the stored maximum is their maximum. It is true after the first element, maintained by comparing the next element, and yields the desired answer when k=n. This illustrates how a precise algorithm can also be proved correct.

Compare original scanPage 1 of 44
Page 02Analysis foundations

Recurrence relation of an algorithm

Teacher taughtHandwritten

What a recurrence represents

A recurrence defines a sequence using smaller or earlier terms. In general, aₙ=f(aₙ₋₁,aₙ₋₂,…,aₙ₋ₖ), together with enough initial values to determine the sequence. For recursive algorithms, T(n) represents the cost on input size n; recursive calls contribute smaller T terms and the remaining work contributes a nonrecursive term.

The notebook uses recurrences for time analysis, divide-and-conquer design, understanding recursive algorithms, and defining states/transitions in dynamic programming. A recurrence is not complete without a base case.

Binary-search example

On a sorted array, compare the key with the middle element. If unequal, only one half can contain the key. Thus we solve one subproblem of roughly half the size, not both halves.

binarySearch(A, low, high, key):
    if low > high: return -1
    mid = low + (high-low)//2
    if A[mid] == key: return mid
    if key < A[mid]: return binarySearch(A, low, mid-1, key)
    return binarySearch(A, mid+1, high, key)

For the worst case, T(n)=T(⌊n/2⌋)+Θ(1), with T(0)=Θ(1). The constant work is the boundary test, midpoint calculation, comparison, and choice of half. Repeated halving reaches the base case in Θ(log n) levels. The best case, when the first midpoint matches, is Θ(1). Recursive auxiliary space is O(log n); an iterative version uses O(1).

Correction / clarification. Recursive search calls must return their results to the caller. The pseudocode above includes the return keywords that are easy to omit when transcribing the handwriting.
Compare original scanPage 2 of 44
Page 03Analysis foundations

Recurrence examples: search, Fibonacci, merge sort

Teacher taughtHandwritten

Three different recursive structures

AlgorithmRecursive workNonrecursive workRecurrence
Binary searchOne half onlyConstant number of tests/comparisonsT(n)=T(n/2)+Θ(1)
Naïve FibonacciCompute both F(n−1) and F(n−2)Base-case check and additionT(n)=T(n−1)+T(n−2)+Θ(1)
Merge sortSort both halvesMerge all n elementsT(n)=2T(n/2)+Θ(n)
fib(n):
    if n == 0 or n == 1: return n
    return fib(n-1) + fib(n-2)

Fibonacci's value satisfies F(n)=F(n−1)+F(n−2), F(0)=0, F(1)=1. Its naïve algorithm's running time adds a constant operation cost. These are related recurrences but not the same quantity. The naïve algorithm repeatedly recomputes F(n−2), F(n−3), etc.; the later DP chapter removes that repetition.

Merge sort splits an n-element array into two parts of size about n/2. Both recursive calls are needed. Merging scans the two sorted parts once, so its work is cn for a positive constant c. With T(1)=Θ(1), its total is Θ(n log n). Floors/ceilings do not alter this asymptotic result.

Correction / clarification. The merge-cost annotation in the scan restricts c to 0<c≤1. There is no such general restriction: c is a positive constant determined by the operation-count model. Merging two lists with n elements takes at most n−1 key comparisons, but copying, assignments and index updates add other operations. The complete merge cost is Θ(n), not necessarily at most n elementary operations.
Compare original scanPage 3 of 44
Page 04Recurrence methods

Ways to solve recurrence relations

Teacher taughtHandwritten

Three methods in the notebook

Repeated substitution / iteration unfolds a recurrence until a pattern appears. Recursion trees organize recursive costs by levels. The Master method compares the branching work with the work outside recursion for equal-size subproblems.

Complete repeated-substitution procedure

  1. Write the recurrence and its base condition.
  2. Replace each recursive term by the same recurrence applied to that smaller argument.
  3. Expand several times and identify the expression after k steps.
  4. Choose k so that the remaining recursive argument equals the base-case argument.
  5. Substitute that k, including the base-case value.
  6. Simplify any arithmetic or geometric sum.
  7. Check the result against the base condition and the original recurrence.

For n halved each time, solve n/2ᵏ=1. For n reduced by one, solve n−k=1. These produce logarithmic and linear depths respectively; confusing them changes the answer.

Correction / clarification. Many textbooks reserve “substitution method” for guessing a bound and proving it by induction. The notebook uses the phrase for repeated expansion. Both are valid techniques, but expansion suggests a formula whereas induction verifies it.

For an induction proof, state the hypothesis for smaller inputs and insert it into the recurrence. Constants must be chosen to satisfy both the recursive step and base cases. Merely writing a few terms is not always a proof of a guessed pattern.

Compare original scanPage 4 of 44
Page 05Recurrence methods

Substitution method: worked recurrences

Teacher taughtHandwritten

Example 1: T(n)=T(n/2)+c

Assume n is a power of two, c>0, and T(1)=1. Each expansion contributes one more c.

T(n) = T(n/2) + c
     = T(n/4) + 2c
     = T(n/8) + 3c
     = T(n/2^k) + kc.
n/2^k = 1  ⇒  k = log₂ n.
T(n) = T(1) + c log₂ n = 1 + c log₂ n = Θ(log n).

Only one call survives to the next level. The logarithm counts how many times n can be halved before reaching 1.

Example 2: T(n)=2T(n/2)+n

There are now two calls per node. Expanding the recursive term multiplies the number of calls, while the total added work at each level remains n.

T(n) = 2T(n/2) + n
     = 2[2T(n/4) + n/2] + n
     = 4T(n/4) + 2n
     = 8T(n/8) + 3n
     = 2^k T(n/2^k) + kn.

The next page substitutes the base-case depth. Do not discard the 2ᵏ multiplier on the base-case cost; it counts all leaves.

Compare original scanPage 5 of 44
Page 06Recurrence methods

Substitution: merge-sort result and another expansion

Teacher taughtHandwritten

Completing the merge-sort expansion

n/2^k = 1  ⇒  k = log₂ n,  2^k = n.
T(n) = 2^k T(1) + kn
     = n + n log₂ n             (when T(1)=1)
     = n(1 + log₂ n) = Θ(n log n).

The n term is the total leaf cost; the n log₂n term is the work above the leaves. For sufficiently large n, the latter dominates. An O(n log n) answer is a valid upper bound, but Θ(n log n) is the tight result.

The next handwritten recurrence has a coefficient conflict

Correction / clarification. The first line and part of the expansion on page 6 appear to use nT(n−1)+log₂n. Page 7 then treats the coefficient as 1 and obtains a sum of logarithms. Those are different recurrences. The two correct solutions are given explicitly rather than blending incompatible lines.

For the recurrence intended by the following logarithm-sum calculation, T(n)=T(n−1)+log₂n, repeated substitution gives:

T(n) = T(n-2) + log₂(n-1) + log₂n
     = T(n-k) + Σ[j=0 to k-1] log₂(n-j).
Set n-k=1, so k=n-1:
T(n) = T(1) + log₂2 + log₂3 + ... + log₂n.

The full simplification appears on page 7. If a question actually specifies the leading factor n, use the factorial solution there instead.

The growth-order note in the margin

For sufficiently large n, a corrected increasing order for the functions written in the margin is 1 < log n < n < n log n < eⁿ < n! < nⁿ. The constant 1 can be replaced by any fixed positive constant. These are eventual growth comparisons, not inequalities promised at every small input.

Each factor of n! is at most n, so n!≤nⁿ, strictly for n>1. A fixed-base exponential is eventually smaller than n!: for example, the last ⌊n/2⌋ factors alone give n!≥(n/2)⌊n/2⌋, whose logarithm eventually exceeds n.

Correction / clarification. The handwritten order puts nⁿ before n!. Those last two terms must be reversed. Polynomial, exponential and factorial growth are distinct; the base e here is fixed, whereas the base of nⁿ grows with n.
Compare original scanPage 6 of 44
Page 07Recurrence methods

Substitution continuation and a geometric recurrence

Teacher taughtHandwritten

Correct completion: T(n)=T(n−1)+log₂n

T(n) = T(1) + Σ[i=2 to n] log₂i
     = T(1) + log₂(2·3·...·n)
     = T(1) + log₂(n!) = Θ(n log n).

Upper bound: there are at most n terms and each is at most log₂n. Lower bound: the last roughly n/2 terms are each at least log₂(n/2). Therefore the sum is bounded above and below by constant multiples of n log n.

If the printed coefficient n is taken literally

For T(n)=nT(n−1)+log₂n and T(1)=1, divide both sides by n! to obtain S(n)=S(n−1)+(log₂n)/n!, where S(n)=T(n)/n!. Hence:

T(n) = n! [1 + Σ[i=2 to n] (log₂i)/i!].
The bracket is bounded between positive constants:
0 ≤ log₂i ≤ i  for i≥2, and Σ i/i! converges.
Therefore T(n) = Θ(n!), not Θ(n log n).

Geometric recurrence: T(n)=2T(n−1)−1

With T(1)=1 and n>1, expand the negative constant as well as the recursive term:

T(n) = 2[2T(n-2)-1]-1 = 4T(n-2)-2-1
     = 8T(n-3)-4-2-1
     = 2^k T(n-k) - Σ[i=0 to k-1] 2^i.

Set k=n−1 to reach the base. The next page shows the exact cancellation. Do not assume “two recursive terms” forces an exponential result when the stated recurrence has a subtractive term and a special base value.

Correction / clarification. The margin writes log n > n and labels a fixed small value as a constant-time “best case”. For large n, log n < n. Also, solving the stated size-only recurrence gives its growth as n varies; evaluating it once at a small n does not establish an algorithm’s best-case complexity. For natural logarithms, log(n!)=n log n−n+O(log n); another fixed base changes the constant coefficients but not Θ(n log n).
Compare original scanPage 7 of 44
Page 08Recurrence methods

Geometric-series completion

Teacher taughtHandwritten

Evaluating the geometric sum

For r≠1, 1+r+⋯+rᵐ⁻¹=(rᵐ−1)/(r−1). Here r=2 and there are n−1 terms, from 2⁰ to 2ⁿ⁻².

T(n) = 2^(n-1)T(1) - Σ[i=0 to n-2] 2^i
     = 2^(n-1) - (2^(n-1)-1)/(2-1)
     = 1.
Check: T(1)=1; if T(n-1)=1 then 2T(n-1)-1=1.

Thus the mathematical sequence is exactly constant, Θ(1). More generally, if T(1)=b, the solution is 2ⁿ⁻¹(b−1)+1; the base value matters.

Correction / clarification. This algebraic recurrence is not the usual running-time recurrence for a program making two recursive calls. Such a program has a nonnegative local cost, typically T(n)=2T(n−1)+Θ(1), which grows exponentially. Evaluating a recurrence sequence and counting the instructions of a recursive implementation are different tasks.
Compare original scanPage 8 of 44
Page 09Recurrence methods

Recursion-tree method: structure and steps

Teacher taughtHandwritten

How to construct a recursion tree

Write T(n)=aT(n/b)+f(n). The root is the original size-n call. Each internal node creates a children, each of size one b-th of its parent. Label a node by its nonrecursive work f(size), not by the entire T(size), when summing costs.

At level iQuantity
Number of nodesaⁱ
Subproblem sizen/bⁱ
Work per nodef(n/bⁱ)
Total work on that levelaⁱf(n/bⁱ)
Depth to size 1h=log_b n, for exact powers
Number of leavesaʰ=n^(log_b a)
Leaf contributionaʰT(1)

Sum internal levels i=0,…,h−1 and then add the leaf contribution. A tree can be top-heavy, level-balanced, or leaf-heavy. Multiplying “height × root cost” is valid only when each level has that same total cost.

  1. Draw the root and first two expansions.
  2. Find the node count and input size at level i.
  3. Multiply to find the level cost.
  4. Solve for the height.
  5. Evaluate the level-cost sum and leaf cost.

Unequal subproblem sizes need a more careful tree; the equal-size formulas above must not be applied mechanically to every recurrence.

Compare original scanPage 9 of 44
Page 10Recurrence methods

Recursion tree for merge sort

Teacher taughtHandwritten

Merge-sort recursion tree

For T(n)=2T(n/2)+n with n=2ʰ, the tree has:

LevelCallsSize per callTotal merge work
01nn
12n/2n
24n/4n
i2ⁱn/2ⁱn
h=log₂nn1Leaf work nT(1)

There are h internal levels, each costing n. Therefore T(n)=nh+nT(1)=Θ(n log n). For n=8, the merge levels cost 8+8+8=24 units in the simplified recurrence, followed by 8 base units if T(1)=1, giving T(8)=32.

The tree's total number of nodes is O(n), but each node does work proportional to its subarray size. Counting only nodes and ignoring those labels would incorrectly suggest Θ(n) total time. Conversely, only O(log n) recursive frames are live along one root-to-leaf path.

Compare original scanPage 10 of 44
Page 11Recurrence methods

Recursion tree for 3T(n/4) + n²

Teacher taughtHandwritten

Tree for T(n)=3T(n/4)+n²

Assume n=4ʰ. Each node has three children, but every child's size is divided by four. Squaring that smaller size is crucial.

LevelNodesSizePer-node workLevel work
01nn²n²
13n/4n²/16(3/16)n²
29n/16n²/256(3/16)²n²
i3ⁱn/4ⁱn²/16ⁱ(3/16)ⁱn²

The ratio between successive level totals is 3/16<1, so deeper levels get cheaper. Solve n/4ʰ=1: h=log₄n. There are 3ʰ=n^(log₄3) leaves. Because log₄3<2, their Θ(n^(log₄3)) base cost is smaller than the root's n² cost.

The tree is root-dominated. The next page evaluates the sum rather than merely observing the pattern.

Compare original scanPage 11 of 44
Page 12Recurrence methods

Geometric sum of the recurrence tree

Teacher taughtHandwritten

Summing the decreasing level costs

h = log₄n
Internal work = n² Σ[i=0 to h-1] (3/16)^i
              = (16/13)n² [1-(3/16)^h].
Total T(n) = (16/13)n²[1-(3/16)^h] + 3^h T(1).

The bracket is at most 1 and approaches 1 as h grows. The root alone costs n², giving Ω(n²); the whole geometric sum is at most (16/13)n², giving O(n²). The leaf cost is Θ(n^(log₄3)), so the final tight bound is Θ(n²).

Correction / clarification. The handwritten sum emphasizes internal levels. The complete derivation also includes the leaves. They do not change this answer, but omitting them without checking can produce a wrong answer in a leaf-dominated recurrence.

This example contrasts with merge sort: merge sort has equal work per level and an extra logarithmic factor; here the geometric decrease makes the total only a constant multiple of the first level.

Compare original scanPage 12 of 44
Page 13Recurrence methods

Recursion tree for T(n−1) + log n

Teacher taughtHandwritten

Tree for T(n)=T(n−1)+log n

This recursion tree is a single chain, not a branching tree. Its consecutive nonrecursive costs are log n, log(n−1), …, log 2. With base T(1)=0:

T(n) = log n + log(n-1) + ... + log 2 + T(1)
     = log(n!) = Θ(n log n).

The chain has n−1 recursive edges, so its stack depth is Θ(n) if each frame uses constant storage. Time is not merely Θ(n), because the work at each node is logarithmic in that node's current input size.

A direct bound avoids relying on Stirling's formula: the sum is at most n log n, while at least ⌊n/2⌋ of its terms are at least log(n/2). These inequalities prove the matching upper and lower bounds.

Correction / clarification. The scan mixes a “n>0” recurrence condition with a base at n=1. Use n>1 for the recurrence and stop at 1; there is no need to evaluate log 0. The base T(1)=0 here versus T(1)=1 on the earlier page changes only an additive constant.
Compare original scanPage 13 of 44
Page 14Recurrence methods

Master method

Teacher taughtHandwritten

Master method: what each symbol means

The classroom form is T(n)=aT(n/b)+Θ(nᵏ(log n)ᵖ), where a≥1 is the number of equal-size subproblems, b>1 is the shrink factor, k≥0 is the polynomial exponent of the nonrecursive work, and p is a fixed real logarithmic exponent. Stop at a positive constant size; use a cutoff at least 2 when negative powers of log are present.

Compare a with bᵏ, equivalently compare log_b a with k. The tree's leaf growth is n^(log_b a); its outside-work growth is nᵏ(log n)ᵖ.

ComparisonTight solutionReason
a > bᵏΘ(n^(log_b a))Polynomially more growth at the leaves; any fixed logarithmic factor is too small to overcome the polynomial gap.
a = bᵏ, p > −1Θ(nᵏ(log n)^(p+1))The normalized level costs sum like 1ᵖ+2ᵖ+⋯+(log n)ᵖ.
a = bᵏ, p = −1Θ(nᵏ log log n)The normalized sum is harmonic: 1+1/2+⋯+1/(log n).
a = bᵏ, p < −1Θ(nᵏ)The normalized sum converges and the leaves supply Θ(nᵏ).
a < bᵏ, any fixed real pΘ(nᵏ(log n)ᵖ)The polynomial gap makes the outside work dominate, including when p is negative.
Correction / clarification. The notebook gives Θ(nᵏ) in the final case when p<0. That loses a logarithmic factor. For example, T(n)=T(n/2)+n/log n is Θ(n/log n), not Θ(n). The table above uses the corrected result. Also, Θ is not a label for “average case”.

Standard three-case form

For T(n)=aT(n/b)+f(n), set d=log_b a. If f(n)=O(n^(d−ε)) for some ε>0, the result is Θ(nᵈ). If f(n)=Θ(nᵈ), it is Θ(nᵈ log n). If f(n)=Ω(n^(d+ε)) and a·f(n/b)≤c·f(n) for a constant c<1 beyond a cutoff, it is Θ(f(n)). The last inequality is the regularity condition.

Do not use this method for T(n−1), unequal-sized branches without further justification, or a function falling in a gap between the stated cases. Use expansion, a tree, or another theorem instead.

Compare original scanPage 14 of 44
Page 15Recurrence methods

Master-method example

Teacher taughtHandwritten

Worked Master-method example

Given T(n)=2T(n/2)+1, identify a=2, b=2, k=0, p=0. Then bᵏ=1 and a>bᵏ. Apply the leaf-dominated case:

T(n) = Θ(n^(log₂2)) = Θ(n).
Direct check with T(1)=1:
T(n) = 2^h T(1) + Σ[i=0 to h-1] 2^i
     = n + (n-1) = 2n-1, where h=log₂n.

This is not binary search: binary search makes one recursive call, whereas this recurrence makes two. It is not merge sort either: the outside work here is 1, whereas merging contributes n. Both the coefficient of T and the nonrecursive term must be read correctly.

Correction / clarification. The handwritten “average case” annotation beside Θ(n) is not implied by the theorem. The theorem solves the recurrence supplied; the case of an algorithm depends on how that recurrence was obtained.
Compare original scanPage 15 of 44
Page 16Brute force

Brute-force algorithm

Teacher taughtHandwritten

Brute force: direct exhaustive work

Brute force solves a problem by systematically trying the candidates allowed by its definition, checking each candidate, and returning a valid or best one. Its advantages in the notes are simplicity, completeness, and use as a baseline for benchmarking more sophisticated algorithms. Its disadvantage is potentially very high running time.

Completeness depends on enumerating the correct candidate set. A program can be simple and still miss answers if its loops omit valid cases. Brute force is not automatically exponential: linear search is Θ(n), while enumeration of all subsets has 2ⁿ candidates.

Class example: largest product from three arrays

Let A, B, and C each have n elements. Choose one element from each and maximize A[i]·B[j]·C[k]. All n³ triples are checked:

best = -infinity
for i = 0 .. n-1:
    for j = 0 .. n-1:
        for k = 0 .. n-1:
            best = max(best, A[i]*B[j]*C[k])
return best

Time is Θ(n³); auxiliary space is Θ(1). With lengths a,b,c instead of one common n, time is Θ(abc). Initialize to negative infinity or the first valid product—not zero—because every valid product might be negative. Use a sufficiently wide numeric type and define what to do if any array is empty.

Correction / clarification. The heading says three arrays, while the handwritten loop repeats the same array letter. The text above follows the heading. For three distinct elements of one array, enumerate i<j<k instead. That is a different candidate set, although still Θ(n³).

Structural improvement: because the product is multilinear, checking combinations of each array's minimum and maximum is enough after finding those extrema. That reduces this particular problem to Θ(n), but the class loop remains the baseline example of exhaustive enumeration.

Compare original scanPage 16 of 44
Page 17Greedy algorithms

Greedy algorithm and fractional knapsack

Teacher taughtHandwritten

Greedy algorithms and their proof obligations

A greedy algorithm makes the locally best feasible choice and does not reconsider it later. Two properties support an optimality proof: greedy-choice property—some optimum begins with the greedy choice—and optimal substructure—after fixing that choice, the remaining part is an optimal solution of the remaining problem.

The notebook lists fractional knapsack, job sequencing, Dijkstra's shortest paths, Prim's MST, and Kruskal's MST. Their choices differ: highest value density, highest job profit, smallest tentative path distance, or lightest safe connecting edge. A locally attractive rule without a proof can fail.

Fractional knapsack: exact classroom input

A bag has capacity W=17. Item i has profit pᵢ, positive weight wᵢ, and selectable fraction xᵢ∈[0,1]. Maximize Σpᵢxᵢ subject to Σwᵢxᵢ≤17. Fractions are allowed here; they are not allowed in 0–1 knapsack.

ItemProfitWeightProfit/weight
1515
210310/3 ≈ 3.333
31553
4741.75
5818
6933
7422

Sort by decreasing density pᵢ/wᵢ. The order used in the notes is 5,1,2,3,6,7,4. Items 3 and 6 tie; either order gives the same profit because their profit per unit weight is equal. Use unrounded ratios for computation; the decimal table is for reading.

Why density is correct: if a candidate solution takes positive weight from a lower-density item while a higher-density item is not fully taken, exchange a small equal amount of weight toward the higher-density item. Capacity is unchanged and profit does not decrease. Repeating this exchange yields the greedy order.

Compare original scanPage 17 of 44
Page 18Greedy algorithms

Fractional-knapsack solution

Teacher taughtHandwritten

Fractional-knapsack fill, step by step

Let c be remaining capacity and P accumulated profit. Start with c=17 and P=0. Take each item fully if it fits; otherwise take exactly c/w of it and stop.

ItemWeight takenFractionProfit addedCapacity leftTotal profit
5118168
11151513
231101223
35115738
6319447
7214251
421/23.5054.5

The answer is 54.5. In original item order, the fractions are (1,1,1,1/2,1,1,1). Their total weight is 1+3+5+2+1+3+2=17. The last item's density is 7/4, so its two remaining units of capacity contribute (7/4)·2=3.5.

Data kept for implementation

Item:
    id       // original item number
    profit   // p_i
    weight   // w_i > 0
    density  // profit / weight
items = all input records, sorted by decreasing density

Keep original IDs while sorting so selected fractions can be reported in the input order. In C++, cast before division or use floating-point fields; integer division would turn 10/3 into 3. A fraction is weight_taken/weight, not profit/weight.

Sorting takes O(n log n); the fill scan takes O(n). The item array and optional fraction output use O(n) storage. With items already density-sorted, the selection phase is linear.

Compare original scanPage 18 of 44
Page 19Greedy algorithms

Fractional fill loop and job sequencing

Teacher taughtHandwritten

Complete fractional fill loop

fraction[item.id] = 0 for every input item
remaining = W; profit = 0
for each item in decreasing profit/weight order:
    if remaining == 0: break
    takenWeight = min(item.weight, remaining)
    fraction[item.id] = takenWeight / item.weight
    profit += fraction[item.id] * item.profit
    remaining -= takenWeight
return profit, fraction

Assume nonnegative profits and positive weights. A negative-profit optional item should not be selected. A zero-weight item needs separate treatment because its density is undefined.

Job sequencing with deadlines

Each job takes one time slot and earns its profit only if completed by its integer deadline. At most one job occupies a slot. The objective is maximum total profit, not maximum number of jobs. The class data are:

Job1234567
Profit3520181630
Deadline1343212

Sort jobs by decreasing profit: 7,3,4,6,2,1,5. For each job, use the latest free slot at or before its deadline. Scheduling late preserves earlier slots for jobs with tighter deadlines.

Considered jobLatest feasible free slotDecision
72Accept
34Accept
43Accept
61Accept
2None ≤3Reject
1None ≤1Reject
5None ≤2Reject

The chronological schedule is [6,7,4,3], profit 6+30+18+20=74. If only three total slots are allowed, clamp each deadline to 3: jobs 7,3,4 occupy slots 2,3,1 respectively. The schedule [4,7,3] earns 68.

discard jobs with deadline <= 0 or profit <= 0
if no jobs remain or allowed horizon <= 0:
    return empty schedule, 0
n = number of remaining jobs
D = min(largest remaining deadline, n, allowed horizon)
sort jobs by decreasing profit
slot[1 .. D] = empty
for job in jobs:
    for t = min(job.deadline, D) down to 1:
        if slot[t] is empty:
            slot[t] = job
            break
return slot, sum of profits of scheduled jobs

With a linear slot search, time is O(n log n+nD), at most O(n²) when D≤n; space is O(n). This proof/model requires unit processing times. Sorting arbitrary-duration jobs by profit is not the same problem.

Boundary completion for the generic algorithms: assume capacity W≥0 and initialize every item fraction to zero, including items never visited because the bag fills early. An empty item set or W=0 then returns profit zero and the correct all-zero selection. Jobs are optional: nonpositive-deadline jobs are infeasible, and nonpositive-profit jobs may be omitted without reducing the optimum. Check the empty job set before taking its maximum deadline. The allowed horizon is a nonnegative integer (or unbounded); these guards do not change either positive classroom example.

Compare original scanPage 19 of 44
Page 20Greedy algorithms

Dijkstra’s algorithm: pseudocode

Teacher taughtHandwritten

Dijkstra: single-source shortest paths

Given a weighted graph and source s, find the minimum total edge weight from s to every reachable vertex. All edge weights must be nonnegative. Store dist[v], parent[v], and a settled/visited flag. Initialize all distances to ∞ except dist[s]=0; every parent is initially absent.

The min-priority queue stores (distance,vertex). Repeatedly remove the unsettled vertex with smallest tentative distance. Relax each outgoing edge: if dist[u]+w(u,v)<dist[v], improve the estimate and record u as v's predecessor.

for each vertex v:
    dist[v] = infinity; parent[v] = none; settled[v] = false
dist[s] = 0
Q.push((0,s))
while Q is not empty:
    (d,u) = Q.popMinimum()
    if settled[u] or d != dist[u]: continue
    settled[u] = true
    for each edge (u,v,w):
        if not settled[v] and dist[u] + w < dist[v]:
            dist[v] = dist[u] + w
            parent[v] = u
            Q.push((dist[v],v))

This is the ordinary lazy-heap version: inserting an improved pair leaves older pairs in the queue; the stale-entry check discards them. An indexed heap can instead perform decrease-key. Either version must order by distance, not vertex label.

Why settling a vertex is safe

Suppose the smallest tentative-distance vertex u had a shorter undiscovered path. On that path, take the first unsettled vertex x after a settled vertex y. Relaxing y→x would already have given x a distance no larger than the path's prefix. Nonnegative remaining edges imply that prefix is no greater than the alleged shorter distance to u, contradicting the choice of u. Negative edges invalidate this argument.

Recover a path by following parent pointers from the target back to s and reversing the sequence. A vertex whose distance stays ∞ is unreachable, not a very expensive reachable vertex. The typed classroom graph is fully worked in Dijkstra and Prim.

Compare original scanPage 20 of 44
Page 21Greedy algorithms

Dijkstra’s complexity

Teacher taughtHandwritten

Time and memory accounting for Dijkstra

With adjacency lists and an indexed binary min-heap, there are at most V extract-min operations and E successful opportunities for decrease-key. Their costs give O(V log V+E log V)=O((V+E)log V). The distance, parent, and settled arrays each need O(V); adjacency lists need O(V+E); an indexed heap needs O(V).

The class C++ program uses a lazy priority queue instead of a true decrease-key. It may store O(E) queued entries and performs O(E) pushes/pops. Its precise general bound is O((V+E)log(V+E)), often written O((V+E)log V) for simple graphs because E≤V². Total graph-plus-working storage is O(V+E).

A matrix implementation with a linear search for the next minimum instead takes O(V²) time and O(V²) graph storage. Complexity belongs to the chosen implementation, not only to the algorithm's name.

Boundary cases

  • A zero-weight edge is valid; nonnegative does not mean strictly positive.
  • A disconnected graph leaves unreachable vertices at ∞.
  • Do not add a weight to an infinity sentinel; only relax from reached vertices.
  • Use a numeric type and sentinel large enough for any valid path sum.
  • Dijkstra is not generally valid with negative edges, even if no negative cycle exists.

Full class C++ listing and observed output are included in this same file.

Compare original scanPage 21 of 44
Page 22Divide and conquer

Divide and conquer; recursion stack

Teacher taughtHandwritten

Divide, solve, combine

Divide and conquer breaks a problem into smaller subproblems, recursively solves them, and combines their answers. The base case is small enough to solve directly. In binary search only one half is pursued; in merge sort both halves are sorted and then merged.

The recursion stack is a live path

When a function calls itself, the caller's parameters, local state, and return location remain on the call stack. Once the recursive call returns, its frame is removed and the caller continues. Therefore auxiliary stack space is the maximum number of simultaneously active frames multiplied by their per-frame storage.

Call:    f(5) → f(4) → f(3) → f(2) → f(1)
Deepest stack, bottom to top: f(5), f(4), f(3), f(2), f(1)
Return:  f(1) → f(2) → f(3) → f(4) → f(5)

If height is h and every frame stores m units, peak stack space is Θ(hm), or Θ(h) for constant m. A decrement-by-one recursion has h=Θ(n); a halving recursion has h=Θ(log n).

Count arrays separately. Passing a reference to one shared array does not copy the entire array into every frame. Passing vectors by value can change the memory and time analysis. Space counts concurrently live memory, not the total storage allocated over all calls.

Compare original scanPage 22 of 44
Page 23Divide and conquer

Tree-based recursion and space

Teacher taughtHandwritten

Tree-shaped calls do not all coexist

Naïve Fibonacci creates a branching recursion tree: F(5) calls F(4) and F(3); F(4) calls F(3) and F(2); and so on. This produces exponentially many calls in total, but a depth-first evaluation finishes one branch before fully evaluating the next.

F(5)
├─ F(4)
│  ├─ F(3)
│  │  ├─ F(2) → F(1), F(0)
│  │  └─ F(1)
│  └─ F(2) → F(1), F(0)
└─ F(3) → F(2), F(1)

The longest active path decreases its argument by one at each step, so auxiliary stack space is Θ(n), not Θ(2ⁿ). The repeated F(3), F(2), etc. explain the poor time complexity and motivate memoization.

For recursive binary search on an existing n-element array, total storage is Θ(n)+Θ(log n)=Θ(n) if the input is counted, while auxiliary storage is Θ(log n). Both statements can be correct because they measure different things. State the convention in an exam answer.

Compare original scanPage 23 of 44
Page 24Divide and conquer

Merge sort: divide phase

Teacher taughtHandwritten

Merge sort: full classroom split

The input is [3,15,8,1,12,6,19,4,10,17,2,14,9,5], indexed 0–13. For inclusive bounds l,h, use m=l+(h−l)//2 and split into [l,m] and [m+1,h].

RangeMiddleLeft partRight part
0–136[3,15,8,1,12,6,19][4,10,17,2,14,9,5]
0–63[3,15,8,1][12,6,19]
7–1310[4,10,17,2][14,9,5]
0–31[3,15][8,1]
4–65[12,6][19]
7–108[4,10][17,2]
11–1312[14,9][5]

Each remaining pair splits into two singleton arrays. A singleton is already sorted, so recursion stops there. Odd-length parts are allowed; their halves differ in size by at most one. No element is lost or duplicated: the two ranges are disjoint and cover the parent's whole range.

The divide operation is an index calculation, not necessarily a physical copy. Sorting happens when the recursive results are combined on the return journey.

Every final split in the drawing

RangeMiddleLeft singletonRight singleton
0–10index 0: [3]index 1: [15]
2–32index 2: [8]index 3: [1]
4–54index 4: [12]index 5: [6]
7–87index 7: [4]index 8: [10]
9–109index 9: [17]index 10: [2]
11–1211index 11: [14]index 12: [9]

Indices 6 ([19]) and 13 ([5]) already became singleton branches one level earlier. Together these are all fourteen leaves, in original index order; the upward arrows on the following page are the merges of these same branches.

Compare original scanPage 24 of 44
Page 25Divide and conquer

Merge sort: conquer phase and code

Teacher taughtHandwritten

Merging the classroom example back upward

StageSorted results
Pairs[3,15], [1,8], [6,12], [19], [4,10], [2,17], [9,14], [5]
Next level[1,3,8,15], [6,12,19], [2,4,10,17], [5,9,14]
Two halves[1,3,6,8,12,15,19] and [2,4,5,9,10,14,17]
Final merge[1,2,3,4,5,6,8,9,10,12,14,15,17,19]

During a merge, only the smallest unused elements of the two sorted halves need comparison. Whichever is smaller is the smallest remaining element overall. This is the invariant that makes a linear merge possible.

mergeSort(A,l,h):
    if l >= h: return
    m = l + (h-l)//2
    mergeSort(A,l,m)
    mergeSort(A,m+1,h)
    merge(A,l,m,h)

Correctness follows by induction on segment length: lengths 0 and 1 are sorted; the recursive calls correctly sort smaller segments; merging those two sorted segments produces a sorted permutation of the original segment. The next page gives the complete combine routine.

Compare original scanPage 25 of 44
Page 26Divide and conquer

Merge/combine routine and space

Teacher taughtHandwritten

Complete merge/combine routine

merge(A,l,m,h):
    i = l; j = m+1; temp = empty list
    while i <= m and j <= h:
        if A[i] <= A[j]:
            append A[i] to temp; i += 1
        else:
            append A[j] to temp; j += 1
    while i <= m:
        append A[i] to temp; i += 1
    while j <= h:
        append A[j] to temp; j += 1
    for k = 0 .. length(temp)-1:
        A[l+k] = temp[k]

The two remaining-element loops are necessary: after one half is exhausted, the unused suffix of the other half is already sorted. Copying it finishes the merge. The destination index is l+k, not k, because the segment may start inside the array.

Using ≤ in the comparison takes the left element first on equality, preserving the original order of equal keys. That makes this merge-sort implementation stable. A different equality rule can lose stability.

Each merge processes h−l+1 elements in Θ(h−l+1) time. Across recursion levels, T(n)=2T(n/2)+Θ(n)=Θ(n log n), for best, average, and worst cases of this standard implementation. One reusable O(n) buffer plus O(log n) frames gives O(n) auxiliary space. Including the input array still gives O(n) total space.

Correction / clarification. The handwritten leftover loops contain crossed-out break statements. No break belongs inside those copying loops; all remaining elements must be copied.

Complete class C++ implementation.

Compare original scanPage 26 of 44
Page 27Divide and conquer

Divide-and-conquer Min-Max

Teacher taughtHandwritten

Minimum and maximum by divide and conquer

Return a pair (minimum,maximum) for a nonempty segment A[l…h]. One element is both extrema. For two elements, one comparison determines both. For longer segments, obtain the pair for each half and combine with two comparisons.

minMax(A,l,h):
    if l == h: return (A[l],A[l])
    if h == l+1:
        if A[l] <= A[h]: return (A[l],A[h])
        return (A[h],A[l])
    m = l + (h-l)//2
    (min1,max1) = minMax(A,l,m)
    (min2,max2) = minMax(A,m+1,h)
    return (min(min1,min2), max(max1,max2))

Classroom array, expressed as extrema pairs

Using the same 14-element array as merge sort, pair-level results include (3,15), (1,8), (6,12), (19,19), (4,10), (2,17), (9,14), and (5,5). Combining produces (1,15), (6,19), (2,17), and (5,14), then (1,19) and (2,17), and finally (1,19).

Correction / clarification. The scan says “no conquer step needed”. It means no merge of all elements is needed. There is still a constant-time combine step: compare the two minima and the two maxima.

The output is a pair of values, not a sorted array. Time is Θ(n); auxiliary recursion space is O(log n) with balanced splitting and constant-size return pairs.

Compare original scanPage 27 of 44
Page 28Divide and conquer

Min-Max completion; quicksort begins

Teacher taughtHandwritten

Min–max comparison count

For n a power of two with the two-element base case, C(2)=1 and C(n)=2C(n/2)+2. There are n/2 leaf pairs, each using one comparison, and n/2−1 internal combination nodes, each using two. Therefore C(n)=n/2+2(n/2−1)=3n/2−2, compared with 2n−2 comparisons for two separate simple scans. The exact count for arbitrary n depends on the split/base-case structure; Θ(n) always remains.

Quicksort: the first pivot in the class example

Start with [3,15,8,1,12,6,19,4,10,17,2,14,9,5]. Choose the first element, 3, as pivot. Scan i from the left until finding a value greater than 3 and j from the right until finding a value at most 3. Swap those misplaced values while i<j.

OperationArray after operation
Pivot =3 at index 0[3,15,8,1,12,6,19,4,10,17,2,14,9,5]
Swap A[1]=15 with A[10]=2[3,2,8,1,12,6,19,4,10,17,15,14,9,5]
Swap A[2]=8 with A[3]=1[3,2,1,8,12,6,19,4,10,17,15,14,9,5]
Pointers cross; swap pivot with A[2][1,2,3,8,12,6,19,4,10,17,15,14,9,5]

The pivot is now at index 2. Its left segment contains values ≤3, and its right segment values >3. Neither segment is necessarily sorted yet.

Correction / clarification. The notes suggest an infinity sentinel to stop the left scan. The safe implementation below instead checks i≤h before reading A[i]. It needs no extra sentinel element or out-of-range access.
Compare original scanPage 28 of 44
Page 29Divide and conquer

Quicksort pivot placement and recursion

Teacher taughtHandwritten

Recursing after pivot placement

Partition returns the pivot's final index p. Since the pivot is already in its sorted position, recursively sort [l,p−1] and [p+1,h], excluding p.

quickSort(A,l,h):
    if l >= h: return
    p = partition(A,l,h)
    quickSort(A,l,p-1)
    quickSort(A,p+1,h)

For the class example, p=2, so the left recursive call sorts indices 0–1 and the right call indices 3–13. The left side [1,2] is easy, but the right side [8,12,6,19,4,10,17,15,14,9,5] still needs its own partitioning. Choosing the first element afresh makes 8 its next pivot.

Partition invariants

Before the scans finish, inspected values on the left are ≤pivot and inspected values on the right are >pivot. Swapping a left value that is too large with a right value that is small restores the intended sides. When the pointers cross, j identifies the last position assigned to the ≤pivot side. Swapping A[l] with A[j] places the pivot between the two regions.

By induction, recursive sorting of those two smaller regions gives a sorted whole array. Partitioning alone does not fully sort them.

Correction / clarification. This is a two-pointer, pivot-placement partition. Classic Hoare partition returns a split boundary and usually recurses on [l,p] and [p+1,h]; do not mix that recursion rule with a function that returns the pivot’s final position.
Correction / clarification. The page-29 row appears to insert an extra 1 immediately after 8, although the input and preceding swaps contain fourteen elements and only one 1. Partition only permutes elements. The corrected fourteen-element row is [1,2,3,8,12,6,19,4,10,17,15,14,9,5], as shown in the preceding trace; no extra element is inserted.
Compare original scanPage 29 of 44
Page 30Divide and conquer

Quicksort partition and complexity

Teacher taughtHandwritten

Bounded partition pseudocode

partition(A,l,h):
    pivot = A[l]
    i = l+1; j = h
    while true:
        while i <= h and A[i] <= pivot: i += 1
        while j > l and A[j] > pivot: j -= 1
        if i >= j: break
        swap A[i], A[j]
    swap A[l], A[j]
    return j

Each pointer moves in one direction, so one partition costs Θ(n) on an n-element segment. The complete sorting cost depends on partition sizes:

CaseRecurrenceTimeAuxiliary stack
Balanced partitionsT(n)=2T(n/2)+Θ(n)Θ(n log n)Θ(log n)
Consistently extreme pivotT(n)=T(n−1)+Θ(n)Θ(n²)Θ(n)
Uniform random pivot, distinct keysExpected recurrence averaged over pivot ranksExpected Θ(n log n), worst Θ(n²)Expected O(log n), worst O(n) for ordinary recursion

With a first-element pivot, sorted, reverse-sorted, or all-equal inputs can cause extreme partitions in this implementation. Randomizing the pivot protects against fixed input ordering in the distinct-key analysis, but two-way partitioning can still perform poorly on many duplicates. A three-way partition groups equal keys explicitly.

Correction / clarification. The notebook labels quicksort’s best case O(n). That is the cost of one partition, not the whole standard quicksort. Balanced recursion gives Θ(n log n). Also, choosing the midpoint element is not the same as finding the median value and does not guarantee balanced partitions.

The array itself uses Θ(n) input storage. Partition is in-place with O(1) extra variables, but recursion adds the stack costs above. See the first-pivot class code and pivot-choice variant.

Correction / clarification. The handwritten partition places an i<h condition in the right-pointer loop, while the left-pointer loop reads A[i] without a bound. A condition on i cannot protect an access through j. In the bounded version above, test i≤h before A[i], and j>l before A[j], using short-circuit evaluation. The left scan stops at a value >pivot and the right scan at a value ≤pivot; after a swap, those changed values ensure that the next scans advance. Thus even equal keys terminate, although they can still give quadratic sorting time.
Compare original scanPage 30 of 44
Page 31Divide and conquer

Sorting classification

Teacher taughtHandwritten

In-place and out-of-place sorting

An in-place sorting routine rearranges elements in the original array using only a small amount of extra data storage. Bubble sort is a straightforward O(1)-auxiliary-space example. Quicksort's partitioning is in-place, although the recursive version additionally uses a stack. Calling quicksort “in-place” normally excludes that logarithmic expected stack from the array-buffer comparison.

An out-of-place sorting routine uses a separate buffer to hold elements during processing. The standard array merge sort in the class notes uses Θ(n) extra buffer space. This is not “external sorting”: external sorting specifically concerns data too large to fit in main memory.

PropertyMeaningClassroom comparison
In-place vs out-of-placeAmount of additional element-storage required.Quicksort partition vs array merge sort.
Stable vs unstableWhether equal-key records retain their original relative order.Merge sort with left-first equality is stable; ordinary quicksort is not.
Comparison vs non-comparisonWhether ordering is obtained through pairwise comparisons or stronger key assumptions.Merge/quicksort are comparison sorts; counting sort uses a bounded key range.

These are independent classifications. A method can be stable and out-of-place, or in-place and unstable. When reporting space, distinguish the input, temporary arrays, and recursion stack explicitly.

Compare original scanPage 31 of 44
Page 32Dynamic programming

Dynamic programming: core idea

Teacher taughtHandwritten

Dynamic programming: solve a state once

Dynamic programming combines solutions of subproblems while storing reusable results. Its central observation is that a naïve recursive algorithm may repeatedly solve exactly the same state. Reusing that answer removes repeated work; it does not change the meaning of the problem.

The notebook's Fibonacci tree contains repeated F(3), F(2), and F(1) calls. For F(5), the distinct required states are only F(0), F(1), …, F(5). Once each is stored, the answer to a repeated state is a table lookup rather than another subtree.

Two properties

  • Overlapping subproblems: different recursive branches reach the same smaller states.
  • Optimal substructure: an optimal solution can be built from appropriate optimal subproblem solutions. For counting or decision DP, the analogous requirement is a valid recurrence that combines the smaller answers.

Greedy algorithms and DP may both use optimal substructure. Greedy commits to one choice; DP evaluates the relevant alternatives and stores the best/combined result. Divide and conquer usually solves separate subproblems rather than repeatedly revisiting the same states.

A complete DP design checklist

  1. Define exactly what each state means.
  2. List base cases, including empty prefixes and zero target/capacity.
  3. Derive a transition from the allowed final choice.
  4. Choose an evaluation order in which dependencies are ready.
  5. Identify the answer cell.
  6. Store choices or backtrack through the table if an actual solution is required.
  7. Count states × work per state and count stored cells.
Compare original scanPage 32 of 44
Page 33Dynamic programming

DP approaches and bottom-up Fibonacci

Teacher taughtHandwritten

Top-down and bottom-up

Top-down memoization starts with the requested state, recursively requests its dependencies, and caches each answer. Bottom-up tabulation starts from base states and fills larger states in dependency order. Both can evaluate the same recurrence.

Bottom-up Fibonacci

fib(n):
    if n <= 1: return n
    F[0] = 0; F[1] = 1
    for i = 2 .. n:
        F[i] = F[i-1] + F[i-2]
    return F[n]
i01234567
F[i]011235813

There are n−1 additions after initialization, so time is Θ(n) in the unit-cost arithmetic model. An array uses Θ(n) space. The early return handles n=0 without incorrectly writing F[1] into a one-cell array.

Rolling-state version

Only the previous two values are needed to compute the next one. Keep a=F(i−2) and b=F(i−1), calculate next=a+b, then assign a=b and b=next. Time remains Θ(n), while auxiliary space becomes Θ(1). This optimization is safe when the earlier values are not needed later for reconstruction or queries.

Fibonacci values grow rapidly, so a fixed-width integer eventually overflows. The operation-count bound does not guarantee that every n fits in a C++ integer type.

Compare original scanPage 33 of 44
Page 34Dynamic programming

Top-down Fibonacci and time complexity

Teacher taughtHandwritten

Top-down memoized Fibonacci

memo[0 .. n] = unknown
fib(k):
    if k <= 1: return k
    if memo[k] is known: return memo[k]
    memo[k] = fib(k-1) + fib(k-2)
    return memo[k]

Use a separate known flag or an impossible sentinel such as −1 for nonnegative Fibonacci values. Zero is a legitimate answer, so “nonzero means computed” is not a generally safe cache convention.

Why naïve recursion is exponential

Without memoization, T(n)=T(n−1)+T(n−2)+Θ(1). Since T(n−2)≤T(n−1), T(n)≤2T(n−1)+c yields an O(2ⁿ) upper bound. The tighter growth is Θ(φⁿ), where φ=(1+√5)/2. The notebook's replacement of both branches by T(n−1) is an upper-bound simplification, not an exact equality.

Why memoization becomes linear

Each of n+1 states is fully computed once, and each nonbase state performs O(1) work plus lookups/calls to smaller states. Hence Θ(n) time. The memo table is O(n), and the longest chain of active recursive calls is also O(n), giving O(n) auxiliary space overall.

Memoization does not make each first-time call constant-time by itself; it makes the total number of distinct state computations linear.

The full exponential upper-bound expansion

The notebook unfolds a simplified recurrence to expose the geometric sum. To keep the reasoning exact, let U(n) be an upper-bound sequence with U(n)=2U(n−1)+c for n≥1 and choose U(0)=b large enough to cover the Fibonacci base costs. Then T(n)≤U(n), by induction, rather than claiming the two Fibonacci branches have identical costs.

U(n) = 2U(n-1) + c
     = 2[2U(n-2)+c] + c
     = 2²U(n-2) + (2+1)c
     = 2³U(n-3) + (2²+2+1)c
     = 2^k U(n-k) + c Σ[j=0 to k-1] 2^j.
Base boundary: n-k=0, so k=n.
U(n) = 2^n b + c(2^n-1) = (b+c)2^n-c = Θ(2^n).
Therefore T(n) = O(2^n).

With b=1 this reproduces the notebook’s expression 2ⁿ+(2ⁿ−1)c. It proves an upper bound; it does not replace the sharper Θ(φⁿ) result for the original Fibonacci recurrence. The memoized algorithm has a different total-work argument—only n+1 distinct states—not the same exponential tree with a smaller constant.

Compare original scanPage 34 of 44
Page 35Dynamic programming

Memoization, tabulation, and 0–1 knapsack

Teacher taughtHandwritten

Memoization versus tabulation

FeatureMemoizationTabulation
DirectionRequested state → dependenciesBase states → target state
ControlRecursion + cacheLoops in dependency order
States visitedOnly those reached by the recursionUsually all states in the chosen table
Extra stackPresentUsually absent
RequirementRecognize cached states correctlyChoose a valid filling order

0–1 knapsack: exact class example

Each item is either taken once or omitted; no fractions and no repeated copies. Capacity W=8. The five (weight,profit) pairs are (2,3), (3,5), (4,4), (5,7), (6,2).

Define d[i][c] as the largest profit obtainable from the first i items with total weight at most c. The empty item set gives d[0][c]=0. With positive item weights, capacity zero gives d[i][0]=0.

Items considered / capacity012345678
None000000000
1: w=2, p=3003333333
2: w=3, p=5003558888
3: w=4, p=4003558899
4: w=5, p=700355881012
5: w=6, p=200355881012

The answer cell is d[5][8]=12. For example, at item 2 and capacity 5, taking it gives 5+d[1][2]=8, while omitting it gives d[1][5]=3, so the cell is 8. At item 4 and capacity 8, taking it gives 7+d[3][3]=12, beating the previous-row value 9.

Every table cell is a precisely defined subproblem. The table is not a list of independent greedy selections; the previous row encodes all earlier feasible combinations.

Compare original scanPage 35 of 44
Page 36Dynamic programming

0–1 knapsack transition and reconstruction

Teacher taughtHandwritten

Deriving the 0–1 transition

For item i with weight wᵢ and profit pᵢ, every feasible solution either excludes it or includes it exactly once. If wᵢ>c it cannot be included. Otherwise compare the best solution without it against its profit plus the optimum using earlier items and the remaining capacity:

d[i][c] = d[i-1][c]                                      if w_i > c
d[i][c] = max(d[i-1][c], p_i + d[i-1][c-w_i])             if w_i <= c

The previous row i−1 appears in both choices because the current item cannot be reused. You need not explicitly scan all earlier rows: d[i−1][·] already summarizes them.

Reconstructing the class solution

CellComparisonAction
d[5][8]=12Equals d[4][8]Omit item 5
d[4][8]=12Greater than d[3][8]=9Take item 4; capacity becomes 8−5=3
d[3][3]=5Equals d[2][3]Omit item 3
d[2][3]=5Greater than d[1][3]=3Take item 2; capacity becomes 0

Selected items are 2 and 4, weight 3+5=8, profit 5+7=12. When a cell equals the one above, skipping is sufficient to obtain one optimum; equality does not prove that the item appears in no optimal solution.

i = n; c = W; selected = []
while i > 0:
    if d[i][c] != d[i-1][c]:
        selected.append(i)
        c -= weight[i]
    i -= 1

Time and table space are O(nW), with O(n) reconstruction. This is pseudo-polynomial because W is a numeric capacity, whose binary encoding uses only Θ(log W) bits. For value-only output, use a one-dimensional array and iterate capacities downward, preventing the current item from being used again in the same iteration. Full class implementation.

Compare original scanPage 36 of 44
Page 37Dynamic programming

Coin-change problem

Teacher taughtHandwritten

Minimum-coin change, not number of ways

The class problem is to pay amount B=9 with the fewest coins from denominations {1,2,4,5,7}, with unlimited copies of each denomination. Define d[i][j] as the minimum number of coins needed to make j using the first i coin types.

Base cases: d[i][0]=0, and d[0][j]=∞ for j>0. Infinity means impossible, not zero coins. For coin cᵢ, either omit that denomination or use one copy and retain access to the same row:

d[i][j] = d[i-1][j]                         if c_i > j
d[i][j] = min(d[i-1][j], 1+d[i][j-c_i])      otherwise
Coin types / amount0123456789
None0∞∞∞∞∞∞∞∞∞
Up to 10123456789
Up to 20112233445
Up to 40112122323
Up to 50112112222
Up to 70112112122

The answer is 2 coins: 7+2 or 5+4. A reconstruction that prefers omitting a coin on ties can skip 7 and choose 5+4. A reconstruction preferring use of 7 may choose 7+2. Both are optimal.

The same-row dependency is what permits repeated copies. Fill amounts in increasing order so j−cᵢ is already solved. In one dimension, initialize dp[0]=0 and all other cells to ∞; for each coin c, loop j from c upward and apply dp[j]=min(dp[j],1+dp[j−c]).

Time is O(kB) for k denominations; table space O(kB), or O(B) for the optimized value-only DP. This is also pseudo-polynomial. Greedy largest-coin-first is not correct for arbitrary coin systems: for {1,3,4} and amount 6, greedy uses 4+1+1, while optimum is 3+3.

Correction / clarification. The handwritten table contains inconsistent intermediate cells. The table above is recomputed from the stated minimum-coin recurrence. Counting combinations would use addition of counts, not a minimum; it is a different DP.

Full class C++ implementation.

Reading the arrows in the coin table

The handwritten reconstruction chooses 4 and 5. With rows numbered by the first i denominations, start at d[5][9]=2. The following trace makes both optimal choices explicit.

Current cellChoice / next cellReason
d[5][9]=2Skip 7 → d[4][9]=2A tie permits an optimum without 7.
d[4][9]=2Take 5 → d[4][4]=11+d[4][4]=2; remain in the same row because copies are unlimited.
d[4][4]=1Skip 5 → d[3][4]=15 exceeds the remaining amount 4.
d[3][4]=1Take 4 → d[3][0]=0Amount zero finishes the answer 5+4.
Alternative d[5][9]=2Take 7 → d[5][2]=11+d[5][2]=2 gives another optimum.
d[5][2]=1Skip 7,5,4 → d[2][2]=1None of the larger denominations fits.
d[2][2]=1Take 2 → d[2][0]=0This finishes 7+2.
Compare original scanPage 37 of 44
Page 38Dynamic programming

Longest Common Subsequence

Teacher taughtHandwritten

Longest common subsequence

A subsequence is obtained by deleting zero or more characters without changing the order of the remaining ones. It need not be consecutive; a substring must be consecutive. The LCS problem finds a longest sequence present as a subsequence of both strings.

The notebook uses X=ENGINEERING and Y=GENERATING. Let d[i][j] be the LCS length between the first i characters of Y and first j characters of X. Empty prefixes give d[0][j]=d[i][0]=0.

if Y[i-1] == X[j-1]:
    d[i][j] = d[i-1][j-1] + 1
else:
    d[i][j] = max(d[i-1][j], d[i][j-1])

If final characters match, extend a common subsequence of the shorter prefixes. If they differ, a common subsequence cannot use both final positions as one matching pair; at least one final character is omitted, giving the maximum of the two shorter-prefix states.

Y prefix / X prefix∅ENGINEERING
∅000000000000
G000111111111
E011111222222
N012222222233
E012222333333
R012222334444
A012222334444
T012222334444
I012233334555
N012234444566
G012334444567

The final value is 7. One LCS is ENERING: positions (1,2,6,8,9,10,11) in ENGINEERING and (2,3,4,5,8,9,10) in GENERATING, using one-based indices. Its characters appear in increasing order in both strings.

Time is O(mn), table space O(mn), for lengths m and n. Two rows suffice for the length alone, but recovering a sequence requires retaining suitable reconstruction information or a separate space-efficient reconstruction algorithm. The notebook mentions human gene-sequence analysis as an application.

Correction / clarification. Some arrows and digits in the handwritten LCS grid conflict with the recurrence. For instance, after the Y prefix GENE, the last two columns (X prefixes ENGINEERIN and ENGINEERING) must both be 3, not the apparent 2s: ENE is already a common subsequence. Along a fixed row or column, an LCS length cannot decrease when a character is added. The main table is the complete recomputed grid, using the strings exactly as written.
Compare original scanPage 38 of 44
Page 39Dynamic programming

LCS backtracking

Teacher taughtHandwritten

Reconstructing an LCS from the table

  1. Start at the bottom-right cell (i=m,j=n).
  2. If the two current characters match, append that character and move diagonally to (i−1,j−1).
  3. If they differ, move to whichever of d[i−1][j] and d[i][j−1] is larger.
  4. If the values tie, either direction yields an optimum. Fix a tie rule for deterministic output.
  5. Stop when i=0 or j=0, then reverse the collected characters.
answer = []
while i > 0 and j > 0:
    if Y[i-1] == X[j-1]:
        answer.append(Y[i-1]); i -= 1; j -= 1
    elif d[i-1][j] > d[i][j-1]:
        i -= 1
    else:
        j -= 1
reverse(answer)

For the classroom strings, one backtracking route records G,N,I,R,E,N,E in reverse order. Reversing gives ENERING. The route may move through equal-valued cells before finding its next matched character; an unchanged value is not itself a character to output.

Reconstructing one LCS takes O(m+n) table movements. To enumerate all LCS strings, explore both tied directions and remove duplicate strings. The number of distinct optimal strings can be large, so “all solutions” is a different output-sensitive task from finding one.

Complete class C++ listing includes table construction and one-solution reconstruction.

Every movement for the classroom LCS

Coordinates are (Y-prefix length, X-prefix length), so the starting cell is (10,11). On a mismatch, this route prefers left when the values tie, matching the convention recorded beneath the handwritten grid.

CellY / X characterValueActionNext cell
(10,11)G / G7Record G; diagonal(9,10)
(9,10)N / N6Record N; diagonal(8,9)
(8,9)I / I5Record I; diagonal(7,8)
(7,8)T / R4Up: value above is larger(6,8)
(6,8)A / R4Up: value above is larger(5,8)
(5,8)R / R4Record R; diagonal(4,7)
(4,7)E / E3Record E; diagonal(3,6)
(3,6)N / E2Left: value left is larger or tied(3,5)
(3,5)N / N2Record N; diagonal(2,4)
(2,4)E / I1Left: value left is larger or tied(2,3)
(2,3)E / G1Left: value left is larger or tied(2,2)
(2,2)E / N1Left: value left is larger or tied(2,1)
(2,1)E / E1Record E; diagonal(1,0)

The recorded characters are G,N,I,R,E,N,E. Reverse them to obtain ENERING, length 7. The final coordinate has an empty prefix; no further match can be added. The table shows why skipping A and T in GENERATING does not break the subsequence order.

Compare original scanPage 39 of 44
Page 40Dynamic programming

Subset-sum dynamic programming

Teacher taughtHandwritten

Subset sum as a boolean DP

Given A=[3,2,6,8,5], determine whether some subset sums to 10, using each element at most once. Define d[i][j] as true exactly when the first i elements can form sum j. The empty subset forms zero, so d[i][0]=true; no positive sum can be formed with no elements, so d[0][j]=false for j>0.

d[i][j] = d[i-1][j] OR
          (j >= A[i-1] AND d[i-1][j-A[i-1]])

The first term omits the current element; the second includes it once. The use of row i−1 in both terms enforces the 0–1 restriction.

Included item prefix / sum012345678910
[]TFFFFFFFFFF
[3]TFFTFFFFFFF
[3, 2]TFTTFTFFFFF
[3, 2, 6]TFTTFTTFTTF
[3, 2, 6, 8]TFTTFTTFTTT
[3, 2, 6, 8, 5]TFTTFTTTTTT

The final answer d[5][10] is true. Two solutions are {3,2,5} and {2,8}. Backtracking from d[5][10] can either omit 5 and recover {2,8}, or include 5 and recover {3,2,5}. If both choices are feasible, both branches may lead to solutions.

Time is O(nT), space O(nT) for target T. A one-dimensional value-only implementation updates j from T downward to A[i], or the same element could be used repeatedly. This nonnegative-index table assumes nonnegative values and target; negative values need a shifted-range or set-based state representation.

Correction / clarification. The notebook labels this “SOS / Sum over Subsets”. The actual recurrence is the subset-sum decision DP. It is not the bitmask “sum-over-subsets transform” that aggregates values over all submasks. The reconstructed table also restores the always-true zero-sum column.
Compare original scanPage 40 of 44
Page 41Backtracking / Branch and Bound

Subset-sum backtracking tree

Outside term test
Teacher taughtHandwritten

The same subset-sum problem as a search tree

At depth k, decide whether to include A[k]. A tree node stores (current sum, sum of still-unprocessed values). For [3,2,6,8,5], the root is (0,24). Taking 3 leads to (3,21); omitting it leads to (0,21).

Decision prefixCurrent sumRemaining sumInterpretation
1321Take 3
11519Also take 2
1111113Taking 6 exceeds target: prune
110513Omit 6
1101135Taking 8 exceeds target: prune
110055Omit 8
11001100Take 5: solution {3,2,5}
0101105Omit 3, take 2, omit 6, take 8: solution {2,8}; remaining 5 omitted

The complete selection vectors in input order are 11001 and 01010. A prefix with current sum above 10 is impossible when all remaining values are nonnegative. A prefix with current+remaining<10 is also impossible because even taking everything cannot reach 10.

At most 2ⁿ subsets exist, and the binary search tree contains O(2ⁿ) nodes. With constant-time sum updates, exploration takes O(2ⁿ) excluding output; printing k full n-bit solution vectors adds O(kn). A depth-first recursion uses O(n) working space.

The later consolidated backtracking lesson contains the complete pruned state trace and the corrected include/exclude control flow.

Compare original scanPage 41 of 44
Page 42Backtracking / Branch and Bound

Subset-sum backtracking pseudocode

Outside term test
Teacher taughtHandwritten

Correct include/exclude pseudocode

solve(k,current,remaining):
    if current > target or current + remaining < target: return
    if k == n:
        if current == target: output choice[0..n-1]
        return
    choice[k] = 1
    solve(k+1,current+A[k],remaining-A[k])
    choice[k] = 0
    solve(k+1,current,remaining-A[k])

solve(0,0,sum(A))

Both branches are required. The exclusion branch is not an else attached to “including is feasible”: even when inclusion is possible, a solution may require exclusion. After finishing the include branch, reset choice[k] before searching the exclude branch.

The leaf-based success test above also handles zero-valued elements without dropping distinct zero-extended solutions. For strictly positive inputs, an early current==target success test can return immediately, provided all unprocessed choices are reported as zero.

Pruning uses nonnegativity. With negatives, current>target can later decrease, and current+remaining is not necessarily the maximum achievable sum. Do not reuse these pruning inequalities unchanged in that setting.

The class source is preserved in the C++ library, with its exact assumptions identified. The corrected algorithm here is the main study version.

Compare original scanPage 42 of 44
Page 43Backtracking / Branch and Bound

N-queen: 4-queen state-space tree

Outside term test
Teacher taughtHandwritten

Four queens as a state-space tree

Place four queens on a 4×4 chessboard so that no pair shares a row, column, or diagonal. Assign one queen per row; let x[r] be its column. This construction removes row conflicts. A new queen at (r,c) is safe against an earlier queen (q,x[q]) exactly when c≠x[q] and |c−x[q]|≠|r−q|.

place(row):
    if row == n: output x; return
    for column = 0 .. n-1:
        if safe against all earlier rows:
            x[row] = column
            place(row+1)
            x[row] = unassigned

Using one-based columns, the two complete solutions are [2,4,1,3] and [3,1,4,2]. For the first, the board rows are:

. Q . .
. . . Q
Q . . .
. . Q .

If the first queen is in column 1, trying row 2 in column 3 immediately blocks row 3; trying row 2 in column 4 permits row 3 in column 2 but then blocks row 4. Backtracking returns to row 1 and tries column 2, which leads to the first solution. Column 3 gives its mirror; column 4 mirrors the unsuccessful column-1 branch.

The later N-Queens lesson contains the full safe-prefix table, candidate-count distinctions, and complexity assumptions.

Compare original scanPage 43 of 44
Page 44Backtracking / Branch and Bound

Branch and Bound (BFS)

Outside term test
Teacher taughtHandwritten

Branch and bound on the classroom weighted graph

The goal is a shortest path from S to G. The undirected edges are S–A:3, S–B:6, A–D:2, A–C:7, B–D:3, B–E:6, D–E:2, D–G:4, C–G:2, and E–G:1.

A search node represents an entire partial path and its cost. A complete path gives an upper bound on the optimum. With nonnegative weights, the current path cost is a lower bound on any completion, so a branch whose cost is already at least the best known complete cost cannot improve it.

Path / branchCostMeaning
S→A→D5Promising partial path
S→A→D→G9First complete candidate; bound becomes 9
S→A→C10Already worse than 9; prune
S→B→E12Already worse than 9; prune
S→A→D→E7Still promising
S→A→D→E→G8Improves bound to 8; final shortest path

The answer is S→A→D→E→G with cost 3+2+2+1=8. The notebook shows the bound changing from 9 to 8. The comparison means “can this partial path still beat the incumbent?”, not “does this vertex ever occur elsewhere?”

Correction / clarification. FIFO breadth-first exploration of weighted paths is not ordinary unweighted BFS shortest-path correctness. Branch and bound needs cost bounds and safe pruning. Repeated vertices can be omitted for nonnegative shortest simple paths; blindly revisiting cycles is unnecessary and may prevent termination with zero-cost cycles.

The small tree drawn at the bottom discusses covering edges using vertices: {S,B} is a minimum vertex cover of edges S–A, S–B, B–D, B–E; {S,D,E} is a larger cover. This is vertex cover, not a path visiting all vertices. It is developed in the approximation lesson.

Compare original scanPage 44 of 44
Teacher handwritten continuation

TT-2 / Part-2

Overlapping notebooks reconciled: cleaner Part-2 transcriptions are primary where available; TT-2 scans remain accessible.

Lesson 01 · Search & optimization

Subset Sum with backtracking

Build a binary decision tree, abandon impossible branches, and recover every subset that reaches the target.

11 minSource-linkedTeacher taught

One complete version of the overlapping subset-sum notes

The earlier notebook and TT2 notes use the same instance: values [3,2,6,8,5], target 10. Each item may be selected at most once. Use a binary decision vector in that input order. The two solutions are 11001 ({3,2,5}) and 01010 ({2,8}).

At index k, keep current=sum of selected processed values and remaining=sum of all unprocessed values. Initially (k,current,remaining)=(0,0,24). Including or excluding A[k] always removes A[k] from remaining; only inclusion adds it to current.

Pruning and completeness

  • If current>10, stop: positive remaining values cannot reduce it.
  • If current+remaining<10, stop: even taking every remaining value is insufficient.
  • If current=10, report the current selections and omit all later positive values.
  • Otherwise explore both include and exclude branches.
search(k,current,remaining):
    if current > target or current+remaining < target: return
    if current == target:
        output choice[0..k-1] followed by n-k zeroes
        return
    if k == n: return
    choice[k] = 1
    search(k+1,current+A[k],remaining-A[k])
    choice[k] = 0
    search(k+1,current,remaining-A[k])
Correction / clarification. The TT2 pseudocode places exclusion under an else. That would omit valid solutions whenever inclusion is feasible. The corrected version makes exclusion a separate branch. Early success is safe here because all five values are positive; for zeros, use the leaf-based version in the earlier lesson.

The entire pruned class tree in readable form

Rows are depth-first, inclusion first. Every child reduces remaining by the next item, even when that child is immediately pruned.

Decisions (1=take, 0=omit)Current sumRemaining sumAction
root024Expand include, then exclude
1321Expand include, then exclude
11519Expand include, then exclude
1111113Prune: sum exceeds 10
110513Expand include, then exclude
1101135Prune: sum exceeds 10
110055Expand include, then exclude
11001100Solution 11001
1100050Prune: even all remaining values are insufficient
10319Expand include, then exclude
101913Expand include, then exclude
1011175Prune: sum exceeds 10
101095Expand include, then exclude
10101140Prune: sum exceeds 10
1010090Prune: even all remaining values are insufficient
100313Expand include, then exclude
1001115Prune: sum exceeds 10
100035Prune: even all remaining values are insufficient
0021Expand include, then exclude
01219Expand include, then exclude
011813Expand include, then exclude
0111165Prune: sum exceeds 10
011085Expand include, then exclude
01101130Prune: sum exceeds 10
0110080Prune: even all remaining values are insufficient
010213Expand include, then exclude
0101105Solution 01010
010025Prune: even all remaining values are insufficient
00019Expand include, then exclude
001613Expand include, then exclude
0011145Prune: sum exceeds 10
001065Expand include, then exclude
00101110Prune: sum exceeds 10
0010060Prune: even all remaining values are insufficient
000013Expand include, then exclude
000185Expand include, then exclude
00011130Prune: sum exceeds 10
0001080Prune: even all remaining values are insufficient
000005Prune: even all remaining values are insufficient

Correctness: every subset has a unique include/exclude path. The two pruning conditions reject only impossible completions under the positive-input assumption, so all remaining valid solutions are found. Worst-case exploration is O(2ⁿ), plus output cost; the decision array and active recursion use O(n) space. The DP alternative trades this exponential dependence for pseudo-polynomial O(nT) time.

Original handwritten pages2 pages · Unique topic in the TT-2 notebook; pages kept intact.
TT-2 supporting scanPage 1
TT-2 supporting scanPage 2
↗Alternate/overlapping TT-2 classroom scan2 source pages
Class notes · TT2page 1 of 25
Class notes · TT2page 2 of 25
Lesson 02 · Search & optimization

N-Queens

Place one queen per row while maintaining column and diagonal safety.

9 minSource-linkedTeacher taught

Problem, representation, and constraints

Place n queens on an n×n board with no shared row, column, or diagonal. Assign one queen to each row and store its column in x[row]. For a candidate at (r,c), every earlier row q must satisfy x[q]≠c and |x[q]−c|≠r−q.

The notebook's C(16,4)=1820 counts all ways to choose four squares on a 4×4 board, including attacking arrangements; it is not a probability and not the number of solutions. Restricting to one queen per row gives 4⁴=256 candidates. Also requiring distinct columns leaves 4!=24 permutations before diagonal checks. Only two complete nonattacking arrangements remain.

safe(row,col):
    for q = 0 .. row-1:
        if x[q] == col or abs(x[q]-col) == row-q: return false
    return true

place(row):
    if row == n: output x; return
    for col = 0 .. n-1:
        if safe(row,col):
            x[row] = col
            place(row+1)
            x[row] = unassigned

Complete safe-prefix tree for n=4

Columns below are one-based. Unsafe candidates are excluded by the column/diagonal predicate; a dead end causes return to the previous row.

Already placed columns, by rowSafe columns for next row
[]1, 2, 3, 4
[1]3, 4
[1, 3]Dead end
[1, 4]2
[1, 4, 2]Dead end
[2]4
[2, 4]1
[2, 4, 1]3
[2, 4, 1, 3]Complete solution
[3]1
[3, 1]4
[3, 1, 4]2
[3, 1, 4, 2]Complete solution
[4]1, 2
[4, 1]3
[4, 1, 3]Dead end
[4, 2]Dead end

The solutions are [2,4,1,3] and [3,1,4,2], mirror images. These produce the two boards drawn in the overlapping notebook copies.

Cost and implementation choices

There are at most n! complete distinct-column arrangements, and fewer survive diagonals. A common optimized formulation iterates unused columns with constant-time column/diagonal occupancy checks. For the straightforward class implementation, every partial state loops over all n columns and each safety check can scan O(n) previous positions, so O(n²·n!) is a safe implementation-specific upper bound, excluding board-output cost.

A column-position array needs O(n) storage plus O(n) recursion. The class program stores a full n×n board, so its auxiliary storage is O(n²). With occupancy arrays, the two diagonal identifiers are row−col+(n−1) and row+col. There is one solution for n=1, none for n=2 or 3, and two for n=4. Full class code.

What every rejected branch in the 4-queen tree means

This table expands the crosses in the source tree. For each safe partial placement it tests all four next columns. Row and column numbers are one-based. “Safe” means recurse, not that a complete solution is guaranteed; for example [1,3] is a safe partial placement but has no safe third-row column.

Placed columnsNext rowTry column 1Try column 2Try column 3Try column 4
[]1SafeSafeSafeSafe
[1]2Reject: same column as row 1Reject: diagonal with row 1SafeSafe
[1, 3]3Reject: same column as row 1Reject: diagonal with row 2Reject: diagonal with row 1; same column as row 2Reject: diagonal with row 2
[1, 4]3Reject: same column as row 1SafeReject: diagonal with row 1; diagonal with row 2Reject: same column as row 2
[1, 4, 2]4Reject: same column as row 1; diagonal with row 3Reject: diagonal with row 2; same column as row 3Reject: diagonal with row 3Reject: diagonal with row 1; same column as row 2
[2]2Reject: diagonal with row 1Reject: same column as row 1Reject: diagonal with row 1Safe
[2, 4]3SafeReject: same column as row 1Reject: diagonal with row 2Reject: diagonal with row 1; same column as row 2
[2, 4, 1]4Reject: same column as row 3Reject: same column as row 1; diagonal with row 2; diagonal with row 3SafeReject: same column as row 2
[3]2SafeReject: diagonal with row 1Reject: same column as row 1Reject: diagonal with row 1
[3, 1]3Reject: diagonal with row 1; same column as row 2Reject: diagonal with row 2Reject: same column as row 1Safe
[3, 1, 4]4Reject: same column as row 2SafeReject: same column as row 1; diagonal with row 2; diagonal with row 3Reject: same column as row 3
[4]2SafeSafeReject: diagonal with row 1Reject: same column as row 1
[4, 1]3Reject: same column as row 2Reject: diagonal with row 1; diagonal with row 2SafeReject: same column as row 1
[4, 1, 3]4Reject: diagonal with row 1; same column as row 2Reject: diagonal with row 3Reject: diagonal with row 2; same column as row 3Reject: same column as row 1; diagonal with row 3
[4, 2]3Reject: diagonal with row 2Reject: diagonal with row 1; same column as row 2Reject: diagonal with row 2Reject: same column as row 1

The two boards as searchable text

Columns:   1 2 3 4            Columns:   1 2 3 4
Row 1:     . Q . .            Row 1:     . . Q .
Row 2:     . . . Q            Row 2:     Q . . .
Row 3:     Q . . .            Row 3:     . . . Q
Row 4:     . . Q .            Row 4:     . Q . .

Positions: [2,4,1,3]          Positions: [3,1,4,2]

Each board has one queen per row and column. Their occupied diagonal identifiers r−c and r+c are also pairwise distinct within each board. A failed row causes the recursive call to return and remove the previous row's queen before trying another column.

Original handwritten pages2 pages · Unique N-Queens pages from the TT-2 notebook.
TT-2 supporting scanPage 3
TT-2 supporting scanPage 4
↗Alternate/overlapping TT-2 classroom scan2 source pages
Class notes · TT2page 3 of 25
Class notes · TT2page 4 of 25
Lesson 03 · Search & optimization

Branch and Bound

Turn exhaustive optimization into an ordered search that discards states whose best possible outcome is already hopeless.

9 minSource-linkedTeacher taught

Branch, bound, and incumbent

Branch and bound is an exact optimization method. A search node encodes a partial candidate; branching extends it. The incumbent is the best complete feasible solution found so far. An optimistic bound estimates the best objective value any completion of a node could achieve. If even that optimistic value cannot improve the incumbent, the whole subtree may be discarded.

For minimization, a partial state's lower bound is compared with the incumbent's upper bound. For maximization, reverse the roles: compare an optimistic upper bound with the best known feasible value. A bound that is too optimistic may waste work; a bound that incorrectly rules out a better solution destroys correctness.

Classroom shortest-path example

The undirected graph has S–A(3), S–B(6), A–D(2), A–C(7), B–D(3), B–E(6), D–E(2), D–G(4), C–G(2), E–G(1). Start at S and seek G. The trace first obtains S–A–D–G with cost 9, then improves it to S–A–D–E–G with cost 8. Paths already costing 10 or 12 cannot improve either bound because all edge weights are nonnegative.

bestCost = infinity; bestPath = none
Q = FIFO queue containing ([S],0)
while Q not empty:
    (path,cost) = Q.popFront()
    if cost >= bestCost: continue
    u = last vertex of path
    if u == G:
        bestCost = cost; bestPath = path
        continue
    for each neighbor v of u:
        if v not already in path and cost+w(u,v) < bestCost:
            Q.pushBack((path followed by v, cost+w(u,v)))
return bestPath,bestCost

A FIFO queue implements the breadth-first scheduling in the heading. A priority queue ordered by bounds gives a best-first variant; a stack gives depth-first search. The scheduling policy and the correctness of the pruning bound are separate decisions.

Correction / clarification. Do not mark a graph vertex permanently visited merely because one path reached it: another path may be cheaper. The path-based version above prevents only cycles within that candidate path. It is a clear reconstruction of the class search tree, not an efficient replacement for Dijkstra on nonnegative shortest paths.

Branch and bound can still explore exponentially many candidates when bounds are weak. FIFO storage can be exponential because many live nodes coexist; a depth-first variant saves frontier memory but may find a useful incumbent later. Backtracking normally prunes violated constraints; branch and bound additionally prunes candidates that cannot improve an objective.

Unpacking the costs and crosses in the class search tree

The number beside a search node is the cost of the complete prefix from S, not the weight of its final edge. Thus D below A is labeled 3+2=5, while D below B is labeled 6+3=9. These are different search states even though the endpoint label is the same.

Path prefix / completionCost calculationMeaning of its branch
S→A3Live prefix.
S→B6Live prefix before a useful incumbent exists.
S→A→D3+2=5Promising prefix.
S→A→C3+7=10Cannot improve incumbent 9 or 8.
S→B→D6+3=9Cannot improve an already known complete path of cost 9.
S→B→E6+6=12Cannot improve 9.
S→A→D→A3+2+2=7Revisits A: reject as a cycle; low prefix cost does not make it a useful simple path.
S→A→D→B3+2+3=8Prefix alone is below 9, but B→E adds 6; returning to S or D repeats a vertex.
S→A→D→G3+2+4=9First complete solution shown: incumbent becomes 9.
S→A→D→E3+2+2=7Still below 9, so investigate it.
S→A→D→E→B3+2+2+6=13Prune; cannot improve 9.
S→A→D→E→G3+2+2+1=8Improve incumbent from 9 to 8.

The crossed return-to-A twig in the handwritten tree illustrates why a vertex may reappear in a raw expansion. The simple-path pseudocode above prevents this revisit immediately. If repeated vertices were allowed, the displayed A-prefix of cost 7 would generate D at 9 and C at 14, neither improving a bound of 9. Eliminating nonnegative cycles earlier is simpler and avoids pointless search.

Once incumbent 8 is known, all prefixes with cost ≥8 may be discarded if only one optimum is required. If the goal is to enumerate all optimal paths, prune cost>8 instead; a cost-8 prefix followed only by zero-weight edges could produce another optimum. Nonnegative edge weights are essential for using cost-so-far as a lower bound.

Original handwritten page1 page · The source gives a graph/tree sketch; the clarification above supplies the missing framework.
TT-2 supporting scanPage 5
↗Alternate/overlapping TT-2 classroom scan1 source page
Class notes · TT2page 5 of 25
Lesson 04 · Search & optimization

Randomized algorithms

Use random choices to simplify logic, avoid adversarial inputs, or obtain useful probability guarantees.

7 minSource-linkedTeacher taught

What makes an algorithm randomized?

A randomized algorithm deliberately samples random choices during execution. For a fixed input, its running time, output, or both may depend on those choices. This differs from average-case analysis of a deterministic algorithm, where the probability is over input instances rather than internal random bits.

The source page is a sparse topic list (“polynomial”, “exponential”, “approximation”, “randomized”), not a full derivation. The definitions and explanation below complete that topic; they are not claimed to be a verbatim handwritten lecture.

Two guarantee types

TypeCorrectnessRunning timeExample
Las VegasAlways returns a correct answer when it returnsRandom; often analyzed in expectationRandomized quicksort always sorts correctly, but partition balance varies.
Monte CarloMay have a bounded error probabilityWork is bounded by the chosen procedureA repeated randomized yes/no test may trade more trials for a smaller error probability.

For independent trials with failure probability at most p<1, a procedure whose error requires every trial to fail can reduce that probability to at most pᵏ after k repetitions. This multiplication requires independence and the appropriate one-sided/combination rule; it is not automatic for every randomized algorithm.

Randomized quicksort

Choose a pivot uniformly from the current segment, move it to the partition position, partition, and recurse. For distinct keys, each pivot rank is equally likely, so an adversary cannot force bad ranks merely by supplying sorted input. The expected time is Θ(n log n), while worst-case time remains Θ(n²).

One way to see the expected bound is to sort the keys conceptually. Keys of ranks i<j are compared only when one is the first pivot selected from ranks i through j; that probability is 2/(j−i+1). Summing these probabilities over pairs gives Θ(n log n) expected comparisons.

Randomized and approximation algorithms solve different concerns. Randomization describes how choices are made; approximation describes a quality guarantee relative to optimum. An algorithm may be both, either, or neither.

Correction / clarification. After “randomized”, the sparse handwritten list contains one more unclear word resembling “maximized”. It has no accompanying definition, algorithm or example. It is retained here as uncertain source wording, not treated as a separately established algorithm-design category or expanded into an invented lecture.
Original handwritten page1 page · Sparse source page; preserved without inventing a transcription.
TT-2 supporting scanPage 6
↗Alternate/overlapping TT-2 classroom scan1 source page
Class notes · TT2page 6 of 25
Lesson 05 · Approximation & constraints

Approximation ratio & Minimum Vertex Cover

Use a maximal matching to construct a vertex cover whose size is at most twice optimal.

12 minSource-linkedTeacher taught

Approximation ratio

Let C be the objective value of a feasible answer returned by an algorithm and C* the optimum. For a minimization problem the ratio is C/C*; for a maximization problem it is C*/C. Under positive objectives the ratio is at least 1, and 1 means optimal. A ρ-approximation proves that the relevant ratio is at most ρ on every valid instance, not just on one example.

The zero-optimum case requires separate handling rather than division by zero. In vertex cover, an edgeless graph has optimum zero and the algorithm below correctly returns the empty set.

Minimum vertex cover

For an undirected graph G=(V,E), a vertex cover C⊆V contains at least one endpoint of every edge. Minimize |C|. This is not a set that merely touches every vertex, nor a path visiting vertices.

The classroom graph has edges AB, BC, CD, DG, CE, EF, DE, DF. A minimum cover is {B,D,E}: B covers AB/BC, D covers CD/DG/DE/DF, and E covers CE/EF. It is optimal because the three disjoint edges AB, CE, DG require at least three distinct selected endpoints.

Maximal-matching 2-approximation

C = empty set
remaining = all graph edges
while remaining is not empty:
    choose any edge (u,v) in remaining
    add u and v to C
    remove every remaining edge incident to u or v
return C

In the class graph, choose BC first: add B,C and remove AB,BC,CD,CE. Then choose DE: add D,E and remove all remaining edges. The returned cover {B,C,D,E} has size 4, while optimum is 3, so this instance's ratio is 4/3. Different arbitrary-edge choices may return different valid covers.

Proof of the factor two

  1. The selected edges share no endpoints: once an edge is selected, all incident edges disappear. They form a matching M.
  2. Every vertex cover must pick at least one endpoint of each edge in M. Because those edges are disjoint, |C*|≥|M|.
  3. The algorithm selects both endpoints of every edge in M, giving |C|=2|M|.
  4. Thus |C|≤2|C*|.

The matching only needs to be maximal—no more edge can be added—not maximum-cardinality. A linear scan selecting an edge only when both endpoints are still unselected implements the same construction in O(V+E) time, with O(V) selection state in addition to the graph.

Implementation exercise retained from the notes

The class note ends with an exercise to implement the approximation procedure. A direct efficient version need not physically delete edges: scan each edge (u,v) once; if neither endpoint has already been selected, select both. At that moment the edge is still uncovered, and selecting both endpoints has the same effect as deleting all incident edges from the remaining-edge set. The edge order determines which maximal matching is built.

selected[v] = false for every vertex v
cover = []
for each undirected edge (u,v) in the chosen edge order:
    if not selected[u] and not selected[v]:
        selected[u] = selected[v] = true
        append u and v to cover
return cover

Use a set or Boolean selection array so a vertex is never printed twice. For the classroom order beginning BC,DE, the output is B,C,D,E. Test an empty graph, a single edge, a star, and the given seven-vertex graph; verify every edge has a selected endpoint. A maximum matching is not required for the factor-two guarantee.

Original handwritten pages2 pages · Cleaner of the two overlapping scans.
Primary clean scanPage 1
Primary clean scanPage 2
↗Alternate/overlapping TT-2 classroom scan2 source pages
Class notes · TT2page 7 of 25
Class notes · TT2page 8 of 25
Lesson 06 · Approximation & constraints

M-Coloring by backtracking

Assign one of m colors to each vertex so adjacent vertices never share a color.

8 minSource-linkedTeacher taught

Coloring a graph with at most m colors

Assign a color from {1,…,m} to each vertex of an undirected simple graph so that endpoints of every edge receive different colors. The decision problem asks whether such an assignment exists; an enumeration version prints all assignments. It is not necessary that every available color be used.

The class graph is the cycle A–B–C–D–A with diagonal A–C, using colors red, green, blue. The triangle A–B–C requires three distinct colors. Since D is adjacent to A and C but not B, D can have B's color.

ABCDResult
RedGreenBlueGreenValid
RedBlueGreenBlueValid
RedRedBlueGreenInvalid: A–B conflict
RedBlueRedBlueInvalid: A–C conflict

Fixing A=red leaves the two valid assignments above; allowing any of the three labels for A gives six labeled assignments. The graph is not 2-colorable because it contains a triangle.

colorVertex(k):
    if k == numberOfVertices: output colors; return
    for c = 1 .. m:
        if every already-colored neighbor of k has color != c:
            colors[k] = c
            colorVertex(k+1)
            colors[k] = unassigned

The state-space tree branches on the color of the next vertex. A conflicting choice is rejected immediately; a safe partial assignment creates a child. When that child finishes, clear the color before trying the next branch. Trying all safe choices guarantees completeness.

There are at most mⁿ complete assignments. If each attempted color scans an n-entry adjacency-matrix row, an O(n·mⁿ) bound describes the usual search-tree cost for fixed m≥2, plus output costs; a loose O(n²mⁿ) bound is also safe. The matrix stores O(n²), colors O(n), and recursion O(n). A self-loop makes proper coloring impossible and must be rejected explicitly if the input is not guaranteed simple. Full class implementation.

The numbered state-space tree, expanded into decisions

Vertex order is A,B,C,D; trial-color order is R,G,B. The root (node 1) is the empty assignment. A safe choice creates a numbered child; a cross in the source means the attempted choice is rejected and creates no child. The first root branch fixes A=R (node 2).

Current assignmentTry next colorTest / next state
A=RB=RReject: edge A–B has two R endpoints.
A=RB=GAccept: node 3 is (R,G).
A=R,B=GC=RReject: C is adjacent to A.
A=R,B=GC=GReject: C is adjacent to B.
A=R,B=GC=BAccept: node 4 is (R,G,B).
A=R,B=G,C=BD=RReject: D is adjacent to A.
A=R,B=G,C=BD=GAccept: node 5 is solution (R,G,B,G). D is not adjacent to B.
A=R,B=G,C=BD=BReject: D is adjacent to C.
A=RB=BAfter undoing the previous branch, accept node 6: (R,B).
A=R,B=BC=RReject: edge A–C.
A=R,B=BC=GAccept: node 7 is (R,B,G).
A=R,B=B,C=GD=RReject: edge A–D.
A=R,B=B,C=GD=GReject: edge C–D.
A=R,B=B,C=GD=BAccept: node 8 is solution (R,B,G,B).
A=R,B=BC=BAfter undoing D and C=G, reject: edge B–C.
EmptyA=GThe next root branch begins (node 9 in the clean sketch). A=B is explored afterward.

The symmetry of color names gives the remaining solutions, but not additional unlabeled color patterns. With three distinct labels, the complete output in A,B,C,D order is:

ABCD
RGBG
RBGB
GRBR
GBRB
BRGR
BGRG

The triangle forces A,B,C to use all three labels; D must copy B. Hence there are exactly 3·2·1=6 solutions. This argument proves that the table is complete rather than merely listing six examples.

Original handwritten page1 page · Cleaner shared-topic page selected as the base.
Primary clean scanPage 3
↗Alternate/overlapping TT-2 classroom scan1 source page
Class notes · TT2page 12 of 25
Lesson 07 · Flow, matching & paths

Network-flow foundations

Understand capacity, conservation, flow value, and the residual graph before augmenting anything.

10 minSource-linkedTeacher taught

Flow network and feasible flow

A flow network is a directed graph with nonnegative capacity c(u,v) on each edge, a source s, and a sink t. In the ordinary edge-flow convention, f(u,v) is the nonnegative amount carried by that directed edge.

  1. Capacity: 0≤f(u,v)≤c(u,v) on every original edge.
  2. Conservation: for every vertex u other than s,t, Σ incoming f = Σ outgoing f.
  3. Flow value: |f|=Σᵥf(s,v)−Σᵥf(v,s), equal to the sink's net inflow.

When no edges enter the source or leave the sink, the value simplifies to source outflow or sink inflow, as in the class example. Conservation still includes edges incident to s or t when summing a nonterminal vertex's incident flow.

Correction / clarification. The notes also state f(u,v)=−f(v,u). That is the alternative signed net-flow convention, not an additional condition on two separately nonnegative directed-edge flows. Do not impose both conventions simultaneously. With signed net flow F, capacity bounds become −c(v,u)≤F(u,v)≤c(u,v).

Bottleneck and residual capacity

On a path s→a→b→t with capacities 10,2,15 and zero initial flow, at most min(10,2,15)=2 units can be sent. The narrowest residual edge is the bottleneck.

After sending δ units through an edge, its forward residual capacity decreases by δ and its reverse residual capacity increases by δ. A reverse residual edge means “cancel part of a previous choice”, not that the physical network gained a new original pipe.

Path edge after sending 2Forward residualReverse cancellation capacity
s→a, capacity 1082
a→b, capacity 202
b→t, capacity 15132

If opposite-direction original edges both exist, the aggregate residual capacity from u to v is c(u,v)−f(u,v)+f(v,u) in the nonnegative edge-flow convention. Use distinct residual edge records or accumulate both contributions correctly.

Cuts and the stopping certificate

An s–t cut partitions V into S and T with s∈S and t∈T. Its capacity is the sum of capacities of original edges directed S→T. Every feasible flow is at most every cut capacity: net flow across the cut equals |f| and cannot exceed that capacity. When no residual s→t path exists, let S be the vertices reachable from s in the residual graph; the resulting cut has capacity equal to the current flow. Thus the flow is maximum and the cut is minimum.

The notebook's four constraints, written with explicit summation domains

Let c(u,v)=0 when the original directed edge is absent, and let f(u,v)=0 on such absent edges. In the nonnegative edge-flow convention, the complete conservation equation is

For each u ∈ V \ {s,t}:
    Σ[x ∈ V] f(x,u) = Σ[y ∈ V] f(u,y)

Source net outflow = Σ[v ∈ V] f(s,v) - Σ[v ∈ V] f(v,s)
Sink net inflow   = Σ[v ∈ V] f(v,t) - Σ[v ∈ V] f(t,v)
These two quantities are equal and define |f|.
Correction / clarification. The handwritten summation excludes s and t from the summation indices as well as excluding them from the conservation vertex. Only the vertex u is excluded. Its incoming/outgoing sums must still include edges connecting u to the source or sink. For example, at a in s→a→b→t, incoming f(s,a) cannot be omitted.

The notebook also lists simultaneous/parallel flow: flow can use several routes at once, provided every edge capacity and every intermediate vertex's conservation equation is respected. Sending flow is not choosing just one permanent source-to-sink path.

The alternate bottleneck drawing: capacity 20 rather than 15

The clean introductory example uses capacities 10,2,15. A later TT2 drawing separately uses s→a→b→t with capacities 10,2,20. After sending 2, its labels are 2/10, 2/2, 2/20. The bottleneck is still 2, but the final forward residual is 18, not 13.

Directed pairOriginal flow / capacityForward residualReverse residual
s→a2/108a→s: 2
a→b2/20b→a: 2
b→t2/2018t→b: 2

The same later sketch adds a lower-left vertex (handwritten label p), a vertex d, and a lower vertex c. Its visible additional labels are s→p: 0/15, p→b: 8, a→d: 4, and d→t: 7; it also draws s→c and c→b without readable capacity labels. These are a separate, unfinished residual-routing illustration, not the six-vertex 23-unit worked network below.

Correction / clarification. The lower sketch does not specify all capacities or all reverse edges. Its displayed 8 and 18 are residual amounts after the initial two units; 0/15 uses flow/capacity notation. Do not infer an exact maximum flow for that incomplete sketch or silently supply missing numbers. The fully specified 10,2,20 chain above can be solved exactly.
Original handwritten page1 page · Cleaner shared-topic page selected as the base.
Primary clean scanPage 4
↗Alternate/overlapping TT-2 classroom scan3 source pages
Class notes · TT2page 9 of 25
Class notes · TT2page 10 of 25
Class notes · TT2page 11 of 25
Lesson 08 · Flow, matching & paths

Ford–Fulkerson & Edmonds–Karp

Repeatedly find an augmenting path, push its bottleneck, and update the residual graph.

14 minSource-linkedTeacher taught

Ford–Fulkerson method and Edmonds–Karp algorithm

Start with zero flow. Find a source-to-sink path using only positive residual capacities. Send the path's minimum residual capacity, update both forward and reverse residual capacities, and repeat. Ford–Fulkerson does not specify how to choose the path; Edmonds–Karp specifies BFS, choosing a path with the fewest edges, not the largest bottleneck or smallest total weight.

residual[u][v] = sum of original capacities from u to v, for every ordered pair
                 // zero only when no original u-to-v edge exists
                 // never erase a real opposite-direction capacity
total = 0
while findPathInResidualGraph(s,t,parent):    // BFS for Edmonds–Karp
    delta = infinity
    for each edge (u,v) on the recovered path:
        delta = min(delta,residual[u][v])
    for each edge (u,v) on the recovered path:
        residual[u][v] -= delta
        residual[v][u] += delta
    total += delta
return total

Complete class network

Directed edgeCapacity
s→v₁16
s→v₂13
v₁→v₂10
v₂→v₁4
v₁→v₃12
v₃→v₂9
v₂→v₄14
v₄→v₃7
v₃→t20
v₄→t4

Augmentations in the handwritten trace

StepResidual pathResidual capacities on pathBottleneckFlow value
1s→v₁→v₃→t16,12,201212
2s→v₂→v₄→t13,14,4416
3s→v₁→v₂→v₄→v₃→t4,10,10,7,8420
4s→v₂→v₄→v₃→t9,6,3,4323

This is a valid Ford–Fulkerson path sequence. It is not the BFS path sequence: at step 3, the shorter residual path s→v₂→v₄→v₃→t already exists. Edmonds–Karp can take 7 units on that path after the first two augmentations and reach 23 in three augmentations.

Final edge-flow table and independent check

EdgeFinal flow / capacity
s→v₁16/16
s→v₂7/13
v₁→v₂4/10
v₂→v₁0/4
v₁→v₃12/12
v₃→v₂0/9
v₂→v₄11/14
v₄→v₃7/7
v₃→t19/20
v₄→t4/4

Source outflow is 16+7=23; sink inflow is 19+4=23. At v₂, inflow 7+4=11 equals its outgoing 11. At v₄, incoming 11 equals 7+4. No residual path reaches t.

The cut S={s,v₁,v₂,v₄}, T={v₃,t} has crossing capacities 12+7+4=23. This supplies an upper bound matching the feasible flow, proving optimality rather than merely reporting the last computed value.

Complexity with assumptions

For integral capacities, each Ford–Fulkerson augmentation increases the integer flow value by at least 1; with O(E) path search, time is O(E|f*|), which depends on numeric capacity. Arbitrary irrational capacities can lead to nontermination with unsuitable path choices.

Edmonds–Karp's BFS distances never decrease. When a directed residual edge becomes a bottleneck, is later reversed, and becomes a bottleneck again, the relevant distance must have increased. Each edge is critical only O(V) times, giving O(VE) augmentations. With adjacency lists, O(E) work per BFS gives O(VE²). The class matrix implementation scans V potential neighbours per visited vertex, so O(V²) per BFS gives the implementation-specific bound O(V³E); matrix storage is O(V²). Full class code.

Residual-initialization clarification. In this aggregate matrix, the initial v₁→v₂ entry is 10 and v₂→v₁ is 4. Reverse cancellation capacity starts at zero, but an existing opposite-direction original edge does not. Initializing every reverse matrix entry to zero would erase real capacity. In a tagged-edge adjacency list, by contrast, each original edge has its own separate zero-capacity cancellation partner.

The class pseudocode in its original edge-flow form

The notebook describes f, the amount actually sent, while the earlier pseudocode stores residual capacities. Both representations describe the same augmentation. For a residual path p, its bottleneck is cf(p)=min{cf(u,v):(u,v) lies on p}. Do not confuse the capacity of one edge with the minimum for the whole path.

FORD_FULKERSON_EDGE_FLOW(G,s,t):
    for every original edge e: f[e] = 0
    while a positive-capacity residual s-to-t path p exists:
        delta = minimum residual capacity of an arc on p
        for each tagged residual arc a on p:
            e = the original edge associated with a
            if a is the forward residual arc of e:
                f[e] = f[e] + delta
            else:                       // cancellation arc of e
                f[e] = f[e] - delta
    return f

A forward residual arc for original e=(u,v) has capacity c(e)−f(e); its paired reverse arc (v,u) has capacity f(e). The notebook expresses the update as “if (u,v) is an original edge, add; otherwise subtract from f(v,u).” That shorthand is safe only when the residual arc's original-edge identity is unambiguous.

Correction / clarification. The worked network has both v₁→v₂ and v₂→v₁. A residual step v₂→v₁ could use unused original capacity, cancel flow on v₁→v₂, or combine both. An endpoint-membership test alone cannot identify which. The edge-flow transcription therefore tags each residual arc with its original edge; the aggregate residual-matrix version instead adds both contributions.

Every original-graph state from the class trace

Each cell is flow/capacity. Column k means after augmentation k. Keeping a zero-flow edge in the table matters: it is still an available original connection. This is the text equivalent of all the original-graph drawings.

Original edgeInitialAfter 1 (+12)After 2 (+4)After 3 (+4)After 4 (+3)
s→v₁0/1612/1612/1616/1616/16
s→v₂0/130/134/134/137/13
v₁→v₂0/100/100/104/104/10
v₂→v₁0/40/40/40/40/4
v₁→v₃0/1212/1212/1212/1212/12
v₃→v₂0/90/90/90/90/9
v₂→v₄0/140/144/148/1411/14
v₄→v₃0/70/70/74/77/7
v₃→t0/2012/2012/2016/2019/20
v₄→t0/40/44/44/44/4

Every residual-graph state, including reverse arcs

In each matrix, the row is the origin and the column is the destination. A positive cell means an available residual arc; zero means no arc in that direction. These are residual capacities, not flow/capacity fractions. The aggregate formula is r(u,v)=c(u,v)−f(u,v)+f(v,u).

Residual state 0: total flow 0

From / tosv₁v₂v₃v₄t
s01613000
v₁00101200
v₂0400140
v₃0090020
v₄000704
t000000

Residual state 1: total flow 12

From / tosv₁v₂v₃v₄t
s0413000
v₁12010000
v₂0400140
v₃0129008
v₄000704
t0001200

Residual state 2: total flow 16

From / tosv₁v₂v₃v₄t
s049000
v₁12010000
v₂4400100
v₃0129008
v₄004700
t0001240

Residual state 3: total flow 20

From / tosv₁v₂v₃v₄t
s009000
v₁1606000
v₂480060
v₃0129044
v₄008300
t0001640

Residual state 4: total flow 23

From / tosv₁v₂v₃v₄t
s006000
v₁1606000
v₂780030
v₃0129071
v₄0011000
t0001940

After step 3, r(v₂,v₁)=4−0+4=8: four units of unused original v₂→v₁ capacity plus four units that could cancel v₁→v₂. It is not merely 4. After step 4, residual reachability from s goes to v₂ (capacity 6), then v₁ (8) and v₄ (3), but cannot reach v₃ or t. Thus the final reachable set is exactly {s,v₁,v₂,v₄}.

Flow conservation checked at every intermediate vertex

VertexFinal inflowFinal outflow
v₁16+0=164+12=16
v₂7+4+0=110+11=11
v₃12+7=190+19=19
v₄117+4=11

The notebook's running-time line “E × maxflow + V” separates repeated searches from initialization. With integral capacities, there are at most |f*| successful augmentations. A full adjacency-list implementation has initialization/search bookkeeping as well; a safe explicit form is O((V+E)(|f*|+1)). On the source-reachable non-isolated graph, the usual simplified bound is O(E|f*|), with initialization understood. The BFS choice yields the capacity-independent Edmonds–Karp bound derived above.

Original handwritten pages4 pages · Worked residual graphs plus both complexity pages.
Primary clean scanPage 5
Primary clean scanPage 6
Primary clean scanPage 7
Primary clean scanPage 8
↗Alternate/overlapping TT-2 classroom scan2 source pages
Class notes · TT2page 13 of 25
Class notes · TT2page 14 of 25
Lesson 09 · Flow, matching & paths

Maximum bipartite matching

Reduce one-to-one assignment to unit-capacity flow and use augmenting paths to repair earlier matches.

10 minSource-linkedTeacher taught

Matching and the flow reduction

A bipartite graph splits its vertices into disjoint sets L and R, with edges only between the sets. A matching is a set of edges with no shared endpoint. A maximum matching has largest cardinality; a merely maximal matching cannot be extended by directly adding an edge but may be smaller.

Build a unit-capacity flow network: add s→u for every u∈L, orient each allowed pair u→v from L to R, and add v→t for every v∈R. All capacities are 1. Integral maximum flow selects whole pairs, and the source/sink unit edges prevent any vertex from being matched twice. The maximum-flow value equals the maximum matching size.

Exact class graph

Left vertexAllowed right partners
A1,2
B1
C2,3
D3,4,5
E5

Why reverse residual edges matter

StepAugmenting pathMatching after augmentation
1s→A→1→tA–1
2s→B→1→A→2→tB–1, A–2
3s→C→3→tB–1, A–2, C–3
4s→D→4→tB–1, A–2, C–3, D–4
5s→E→5→tB–1, A–2, C–3, D–4, E–5

At step 2, 1→A is a reverse residual edge. It cancels A–1, freeing 1 for B, while A moves to 2. A greedy algorithm that permanently fixed A–1 would incorrectly leave B unmatched.

The alternate notebook first chooses D–5 and later uses s→E→5→D→4→t to reroute D. That produces the same final matching. These are compatible traces, not conflicting answers.

The answer is 5, with A→2, B→1, C→3, D→4, E→5. Five is also an upper bound because there are only five vertices on each side. Hence the matching is maximum. For unit-capacity augmenting-path search, at most min(|L|,|R|) augmentations are needed; straightforward DFS/BFS augmentation takes O(VE) time on the bipartite graph.

Reading every original/residual matching diagram

Every source-to-left edge, allowed left-to-right edge, and right-to-sink edge has capacity 1. At any stage: a currently matched pair has forward capacity 0 and reverse capacity 1; an unmatched allowed pair has forward capacity 1 and reverse capacity 0. A matched left vertex has s→left residual 0 and left→s residual 1. A matched right vertex has right→t residual 0 and t→right residual 1. Unmatched vertices have the opposite terminal-edge capacities.

The table below, together with the allowed-pair table, specifies every edge in each diagram. Each selected pair carries 1/1; all other allowed pairs carry 0/1. A dash denotes an unmatched vertex.

After stepABCDEUnmatched right vertices
0—————1,2,3,4,5
11————2,3,4,5
221———3,4,5
3213——4,5
42134—5
521345None

The second augmentation changes A–1 from 1 to 0, B–1 from 0 to 1, and A–2 from 0 to 1. Its net increase is one pair, not three. At the end, all five source-to-left edges are saturated, so s has no outgoing residual edge; no augmenting path remains.

Alternate notebook order, fully expanded

StepPathChanged pairs
1s→A→1→tAdd A–1
2s→B→1→A→2→tRemove A–1; add B–1 and A–2
3s→C→3→tAdd C–3
4s→D→5→tAdd D–5
5s→E→5→D→4→tRemove D–5; add E–5 and D–4
Correction / clarification. The alternate handwritten second path abbreviates the middle as B→1→A and then t. There is no A→t edge in the reduction. The complete residual path must continue A→2→t, as shown in the cleaner version. The last path similarly reroutes D from 5 to 4; it does not match 5 to two left vertices.
Original handwritten pages2 pages · Cleaner matching derivation and final result.
Primary clean scanPage 9
Primary clean scanPage 10
↗Alternate/overlapping TT-2 classroom scan1 source page
Class notes · TT2page 15 of 25
Lesson 10 · Flow, matching & paths

Minimum path cover in a DAG

Cover every vertex exactly once with the fewest vertex-disjoint directed paths.

11 minSource-linkedTeacher taught

Minimum vertex-disjoint path cover in a DAG

A path cover is a collection of directed paths containing every vertex exactly once across the collection. A single vertex is allowed as a path. The objective is the number of paths, not their total length. The reduction below requires a directed acyclic graph and vertex-disjoint coverage.

  1. Make two copies of every vertex: uL (outgoing role) and uR (incoming role).
  2. For each original edge u→v, add bipartite edge uL→vR.
  3. Find a maximum matching M, using a unit-capacity source/left/right/sink network if desired.
  4. The answer is |V|−|M|.

Why the formula is correct

Initially each vertex is its own path, giving |V| paths. Every selected matching edge joins a predecessor to a successor and reduces the count by one. The matching ensures at most one selected incoming and outgoing edge per original vertex. Acyclicity prevents these links from forming a cycle, so they form disjoint paths. Conversely, a cover of k paths uses |V|−k links, which form a matching in the split graph. Maximizing links is therefore equivalent to minimizing paths.

Five-vertex class example

Edges are A→B, A→C, B→D, C→D, D→E. Choose matching edges AL–BR, BL–DR, DL–ER. The matching has size 3, so the minimum cover has 5−3=2 paths: A→B→D→E and the singleton C. Another optimum is A→C→D→E plus singleton B.

Both B→D and C→D cannot be chosen together because they share D's right copy. This explains why no one path can cover both incoming branches in the original graph.

Seven-vertex example in the alternate notebook

Edges A→B→C split into C→D→E and C→F→G. Two minimum covers are {A→B→C→D→E, F→G} and {A→B→C→F→G, D→E}. Each uses five matching links, so 7−5=2 paths.

Recovering the paths

For every matched edge uL–vR, set successor[u]=v and predecessor[v]=u. Start at vertices with no selected predecessor and follow successors until none remains. Do not print the artificial source, sink, or L/R copies in the final original-graph paths.

Five-vertex split-network trace, without the drawings

The left side is {AL,BL,CL,DL,EL}; the right side is {AR,BR,CR,DR,ER}. The only cross edges are AL→BR, AL→CR, BL→DR, CL→DR, DL→ER. Add s→every left copy and every right copy→t, all with capacity 1.

StepUnit augmenting pathSelected original linksOriginal-vertex path components
0NoneNoneA; B; C; D; E
1s→A_L→B_R→tA→BA→B; C; D; E
2s→B_L→D_R→tA→B, B→DA→B→D; C; E
3s→D_L→E_R→tA→B, B→D, D→EA→B→D→E; C

BR and BL are different vertices of the matching network, so selecting both AL–BR and BL–DR is allowed. In the original graph this makes B an internal path vertex with one predecessor and one successor. By contrast, selecting BL–DR and CL–DR is forbidden: D would have two predecessors.

Only BR,CR,DR,ER can receive cross edges, but AL is the sole possible predecessor for both BR and CR, so at most one of those two can be matched. Together with at most one DR and one ER, the matching size is at most 3. The trace reaches 3, proving that two paths are necessary and sufficient.

Recover the cover, step by step

for each original vertex v:
    predecessor[v] = NONE; successor[v] = NONE
for each matched pair (u_L,v_R):
    successor[u] = v
    predecessor[v] = u
for each original vertex start with predecessor[start] == NONE:
    path = []
    v = start
    while v != NONE:
        append v to path
        v = successor[v]
    output path

For the selected links, predecessor-free vertices are A and C. Starting at A yields A,B,D,E; starting at C yields C alone. Reconstruction is O(V) after the matching because every original vertex is visited once. The reduction creates 2V+2 network vertices and E+2V forward edges; a simple augmenting-path matching implementation takes O(V(E+V)) time, plus O(V+E) storage with adjacency lists.

Original handwritten pages2 pages · Cleaner transformation and worked answer.
Primary clean scanPage 11
Primary clean scanPage 12
↗Alternate/overlapping TT-2 classroom scan2 source pages
Class notes · TT2page 16 of 25
Class notes · TT2page 17 of 25
Lesson 11 · Security & geometry

RSA public-key cryptography

Generate a public/private key pair from two primes, then encrypt and decrypt by modular exponentiation.

13 minSource-linkedTeacher taught

Plaintext, ciphertext, encryption, and decryption

Plaintext is the original message; encryption transforms it into ciphertext; decryption recovers the message using the appropriate key. The notebook first illustrates a one-letter shift: RAG→SBH and then SBH→RAG. That example is a Caesar shift, not RSA. It introduces the vocabulary before the RSA key-generation steps.

RSA is an asymmetric/public-key construction. A sender encrypts using the recipient's public key; the recipient decrypts using its corresponding private key. Classroom “textbook RSA” operates on an integer message m in the range 0≤m<n.

Key generation, with every quantity defined

  1. Choose distinct primes p and q.
  2. Compute the modulus n=pq.
  3. Compute Euler's totient φ(n)=(p−1)(q−1), the number of residues from 1 through n that are coprime to n.
  4. Choose e with 1<e<φ(n) and gcd(e,φ(n))=1.
  5. Find the modular inverse d of e modulo φ(n): ed≡1 (mod φ(n)).
  6. The public key is (e,n); the basic private-key pair is (d,n). The primes and private exponent are kept secret.

The inverse exists precisely because e and φ(n) are coprime. Writing ed=kφ(n)+1 gives d=(kφ(n)+1)/e. Trying k is manageable for tiny class examples, while extended Euclid computes the inverse systematically.

Exact classroom key calculation

QuantityCalculation
p,q11,13
n11·13=143
φ(n)10·12=120
e7, since gcd(7,120)=1
d(6·120+1)/7=103
Inverse check7·103=721=6·120+1
Public key(7,143)
Private key(103,143)

Extended Euclid gives the same result: 120=17·7+1, so 1=120−17·7. Thus 7⁻¹≡−17≡103 (mod 120).

Encryption and decryption

Encryption: c = m^e mod n
Decryption: m' = c^d mod n
Class message: m=83
c = 83^7 mod 143 = 8
m' = 8^103 mod 143 = 83

The numerical encoding 83 is the classroom plaintext integer. A longer string requires an encoding and appropriate block/padding construction; a whole string cannot simply be substituted into an integer exponent expression. The next lesson expands both modular powers completely.

Why decryption works

Since ed=1+kφ(n), if gcd(m,n)=1, Euler's theorem gives m^φ(n)≡1 (mod n), hence m^ed=m·(m^φ(n))ᵏ≡m (mod n). For a message divisible by p or q, reason modulo each prime: if m≡0 modulo that prime the equality is immediate; otherwise Fermat's theorem applies because ed−1 is divisible by p−1 and q−1. The two congruences combine modulo pq, so decryption also works for those message residues.

Correction / clarification. Some handwritten substitutions use 120 as the encryption/decryption modulus. That is φ(n), used to compute d; the message operations must use n=143. Likewise, the private key is (d,n), not (d,φ(n)). The cleaner notes resolve both conflicts.

What the small example does not establish

The tiny primes are for arithmetic practice, not security. The note's suggestion that five-digit primes are sufficient in real life is incorrect. The value 65537 is commonly discussed as a public exponent e, not as an adequately large RSA prime. Raw textbook RSA is deterministic and lacks the protections required in real encryption/signature schemes; use vetted cryptographic libraries and standardized constructions rather than this classroom formula as an application protocol.

Congruence and remainder notation used in the handwritten calculation

a≡b (mod m) means m divides a−b. It does not mean a=b as ordinary integers. When b is chosen in 0,…,m−1, b is the nonnegative remainder of a on division by m. For example, 721≡1 (mod 120) because 721−1=6·120. Thus the notebook's d·e≡1 condition is equivalent to d·e=kφ(n)+1 for an integer k.

For e=7, the class computes d=(120k+1)/7. Reducing the numerator modulo 7 gives k+1≡0 (mod 7), so the smallest nonnegative suitable k is 6. Then d=721/7=103. Adding any multiple of 120 to d gives another inverse representative, but 103 is the representative in 1,…,119.

The handwritten factor list is 120=1·120=2·60=3·40=5·24=6·20=8·15. These are examples of its divisors, not all its factor pairs: 4·30 and 10·12 complete the positive unordered list. A value e divisible by 2,3 or 5 cannot be coprime to 120. The choice 7 is valid because 120=17·7+1.

Exactly what plaintext 83 encodes

The message label in the notebook begins with S; its example maps that character to integer 83, its ASCII code. The operation 83⁷ mod 143 encrypts this one encoded character, not the entire written multi-character message. The recipient uses the private exponent 103 and the same modulus 143 to recover 83; decoding 83 recovers S. The success condition written in the notes is P′=P.

The clean note identifies 65537=2¹⁶+1 as a commonly chosen public exponent because its binary representation has only two 1 bits, allowing relatively few multiply steps in binary exponentiation. That role is distinct from selecting large secret primes p and q. The separate claim about five-digit primes is not a valid real-world security recommendation, as noted above.

Original handwritten pages2 pages · Cleaner RSA sequence and toy key example.
Primary clean scanPage 13
Primary clean scanPage 14
↗Alternate/overlapping TT-2 classroom scan4 source pages
Class notes · TT2page 18 of 25
Class notes · TT2page 19 of 25
Class notes · TT2page 20 of 25
Class notes · TT2page 21 of 25
↗Alternate RSA notebook copy4 source pages
Class notes · RSApage 1 of 4
Class notes · RSApage 2 of 4
Class notes · RSApage 3 of 4
Class notes · RSApage 4 of 4
Lesson 12 · Security & geometry

Fast modular exponentiation

Compute huge powers without ever constructing the huge integer.

10 minSource-linkedTeacher taught

Reduce while multiplying

To compute aᵉ mod m, do not first construct the enormous integer aᵉ. The identities (a mod m) mod m=a mod m and (ab) mod m=((a mod m)(b mod m)) mod m allow reduction after every multiplication.

Write e in binary: every 1 bit selects a power a^(2ⁱ). Compute those powers by repeatedly squaring modulo m, then multiply only the selected ones.

Class encryption: 83⁷ mod 143

PowerCalculationResidue
83¹8383
83²6889 mod 14325
83⁴25²=625; 625 mod 14353

Since 7=1+2+4, multiply residues: 83·25=2075≡73 (mod143), then 73·53=3869≡8 (mod143). Thus the ciphertext is 8.

Class decryption: 8¹⁰³ mod 143

103=64+32+4+2+1, or binary 1100111₂. The 16 and 8 bits are zero.

ExponentRepeated-square residueUse this power?
18Yes
264Yes
492Yes
827No
1614No
3253Yes
6492Yes

Compute 8·64≡83; then 83·92≡57; then 57·53≡18; then 18·92≡83, all modulo 143. This recovers the plaintext. Equivalently, the notebook's reverse multiplication order gives the same residue.

Square-and-multiply algorithm

modPower(a,e,m):                 // e >= 0, m > 0
    result = 1 mod m
    a = a mod m
    while e > 0:
        if e is odd: result = (result*a) mod m
        a = (a*a) mod m
        e = e//2
    return result

Loop invariant: result·aᵉ is congruent to the original base raised to the original exponent. On an odd exponent, extract one a into result; squaring a and halving the remaining even exponent preserves the product. When e reaches zero, result is the desired residue.

The loop has Θ(log e) iterations for e>0, O(log e) modular multiplications, and O(1) stored machine words. Large-integer bit complexity also depends on the multiplication/modular-reduction method. In C++, a*a can overflow before the modulus is applied; choose a sufficiently wide type or an overflow-safe modular multiplication routine.

For exponent zero, return 1 mod m; for m=1 every result is zero. Negative exponents require a modular inverse and are outside this routine's stated domain.

All intermediate repeated-square arithmetic for decryption

Start with the ciphertext 8. Every row squares the preceding residue and reduces modulo 143:

PowerInteger calculationResidue
8¹88
8²8·8=6464
8⁴64·64=4096=28·143+9292
8⁸92·92=8464=59·143+2727
8¹⁶27·27=729=5·143+1414
8³²14·14=196=1·143+5353
8⁶⁴53·53=2809=19·143+9292

The binary selection table in the notes reads, from high bit to low bit:

Power of two6432168421
Bit of 1031100111
Selected residue9253——92648

The notebook multiplies the selected factors in reverse exponent order. Here are all its intermediate reductions:

8^103 mod 143
 = (92 * 53 * 92 * 64 * 8) mod 143
92 * 53 = 4876 = 34*143 + 14
14 * 92 = 1288 =  9*143 + 1
 1 * 64 =   64
64 *  8 =  512 =  3*143 + 83
Therefore 8^103 mod 143 = 83.
Correction / clarification. The clean scan has a malformed product-reduction rule, mixing different letters/moduli, and one exponent-split line resembles 7=1+2+7. The consistent identities are (ab) mod m=((a mod m)(b mod m)) mod m and 7=1+2+4. The following numerical work in the sources uses the correct 83¹,83²,83⁴ terms.
Original handwritten pages2 pages · Cleaner encryption and decryption arithmetic.
Primary clean scanPage 15
Primary clean scanPage 16
↗Alternate/overlapping TT-2 classroom scan1 source page
Class notes · TT2page 22 of 25
Lesson 13 · Security & geometry

Convex hull with Gift Wrapping

Walk around the outermost points by repeatedly choosing the most counterclockwise candidate.

12 minSource-linkedTeacher taught

Convexity and the convex hull

A set is convex when the segment joining any two of its points lies entirely inside or on its boundary. For a simple polygon, this corresponds to no inward dent and no interior angle greater than 180°. A concave simple polygon has at least one reflex interior angle greater than 180°.

The convex hull of a finite point set is the smallest convex set containing all the points. In the nondegenerate planar case, it is a polygon whose vertices are selected input points. Imagine tightening a rubber band around the set: interior points do not become hull corners.

Orientation, without needing an angle measurement

cross(A,B,C) = (B.x-A.x)*(C.y-A.y) - (B.y-A.y)*(C.x-A.x)
positive: A→B→C is counterclockwise (C is left of directed A→B)
negative: clockwise (C is right of A→B)
zero: collinear

For example, A=(0,0), B=(2,0), C=(1,1) gives cross=2, a left turn. This numerical example explains the predicate; the class drawing itself supplies labels, not numerical coordinates.

Gift wrapping / Jarvis march

  1. Choose the point with minimum x, breaking a tie by minimum y. It must be on the hull.
  2. At current hull point p, choose any other point q as a temporary candidate.
  3. Scan every other point r. If cross(p,q,r)>0, replace q by r. If the cross product is zero, keep the farther point from p.
  4. After the scan, append q as the next hull point and repeat from it.
  5. Stop when the next point is the starting point.
points = distinct input coordinates
if number of points <= 1: return points
start = leftmost-lowest distinct point
p = start; hull = []
repeat:
    hull.append(p)
    q = any distinct point other than p
    for r in all points:
        if cross(p,q,r) > 0 or
           (cross(p,q,r) == 0 and distanceSquared(p,r) > distanceSquared(p,q)):
            q = r
    p = q
until p == start

With this sign convention the chosen directed hull edge has the other points on its right, so this version traverses the boundary clockwise. Reversing the sign produces the opposite orientation; both can be correct if used consistently.

Full labeled class trace

The sketch has exterior labels C,E,F,H,A,B and interior points D,G. Starting at C, the notes initially test candidate D. G,F,H lie to the right of C→D, while E lies to the left, so E replaces D. The remaining tests retain E.

Current pointCandidate testing / reasonChosen next point
CStart with D; G,F,H do not replace it; E makes a left turn and replaces D; B,A do not replace E.E
ECandidate F survives tests against H,G,D,C,B,A.F
FCandidate H survives tests against A,B,C,D,E,G.H
HCandidate A survives tests against B,C,D,E,F,G.A
ACandidate B survives tests against C,D,E,F,G,H.B
BCandidate C survives tests against A,D,E,F,G,H; C is the start.C — close hull

The hull is C→E→F→H→A→B→C; D and G are excluded. The source stack can be read in reverse depending on which end is drawn on top, but it describes this same boundary.

Cost and degeneracies

Every selected hull vertex requires scanning all n input points. For h hull vertices, time is O(nh), hence O(n²) worst case. Output uses O(h); the selection variables use O(1) beyond stored points/output. Remove duplicate coordinates or handle them deliberately. With all points collinear, a vertices-only hull contains the two extremes; one point gives a point and two distinct points give a segment. Choosing the farthest collinear point prevents an interior boundary point from prematurely becoming the next corner.

Full class Jarvis C++ code is retained, including its particular output conventions.

Boundary completion. The explicit initial guard returns an empty hull for no points and the point itself for one distinct coordinate. For two distinct points, the loop visits both and returns to the start. Farthest-collinear selection likewise returns just the two extremes for an all-collinear set. This is the vertices-only convention; repeated input coordinates do not create extra hull corners.

Original handwritten pages3 pages · Cleaner definition, algorithm, and worked trace.
Primary clean scanPage 17
Primary clean scanPage 18
Primary clean scanPage 19
↗Alternate/overlapping TT-2 classroom scan3 source pages
Class notes · TT2page 23 of 25
Class notes · TT2page 24 of 25
Class notes · TT2page 25 of 25
Teacher late-course notes

Geometry, string matching, complexity & graph sheets

Every page/image from the late PDF, string-matching PDF, RSA copy and ZIP has been inspected and mapped.

Late-course classroom notes

Graham Scan

Teacher gave both algorithm steps and a stack/orientation worked drawing.

Teacher taughtHandwritten

Graham scan: sort once, then maintain a convex stack

Graham scan constructs the same planar convex hull as gift wrapping but first orders the points around a pivot. The notebook chooses minimum y, breaking a tie with minimum x. This differs from Jarvis march's leftmost starting convention; either algorithm's stated choice should be followed consistently.

  1. Select the lowest-leftmost pivot A.
  2. Order all other distinct points by increasing polar angle around A.
  3. For equal angles, retain the farthest point if the required output contains only hull corners.
  4. Push A and the first remaining point.
  5. For each next point Z, let Y be the stack top and X the next-to-top. While X→Y→Z is not a strict counterclockwise turn, pop Y and reconsider the new last two points.
  6. Push Z. The stack at the end contains the hull in boundary order.
points = distinct input coordinates
if number of points <= 1: return points
pivot = point with minimum (y,x)
sort other points by increasing polar angle around pivot,
    breaking equal-angle ties by increasing distance from pivot
stack = empty
for point p in that order, including pivot first:
    while stack.size >= 2 and cross(stack[-2],stack[-1],p) <= 0:
        stack.pop()
    stack.push(p)
return stack

The repeated while is essential: one incoming point can invalidate several earlier candidates. An if that pops only once can leave a concave turn. The ≤0 rule excludes collinear interior boundary points; a different boundary-output convention needs deliberate tie handling.

The stack invariant and why a pop is safe

After each insertion, the stack follows the convex boundary of the points processed in angular order, with strict counterclockwise turns. If X→Y→Z turns clockwise or is collinear with Y between the extremes, Y cannot be a required extreme corner once Z is considered. Removing Y restores the possibility of a convex boundary; testing again handles an entire chain of newly internal points.

Readable version of the labeled notebook drawing

The clearer alternate drawing orders candidates B,J,D,E,G,F,C,H,I around pivot A. The visible final boundary is A→B→J→G→H→I→A; C,D,E,F are interior. The sketch has no numerical coordinates, so the labels and stack decisions—not invented cross-product numbers—are the source trace.

Processing stageStack effect
A,B,JInitialize the lower boundary A,B,J.
D then ED is considered; the turn toward E removes the internal D.
GRemove the internal E before retaining G; boundary so far A,B,J,G.
F then CThe interior chain is tested; F is removed when the later angular candidate makes it nonconvex.
HRemove the remaining interior C before keeping H.
IKeep I; final stack A,B,J,G,H,I.

A stack printed by popping gives the reverse order I,H,G,J,B,A. Reversing boundary orientation does not change the set of hull corners.

Complexity and robust comparisons

Pivot selection is O(n), sorting is O(n log n), and the stack pass is O(n): each point is pushed once and popped at most once. Total time is O(n log n), auxiliary/output storage O(n). Comparing polar directions via cross products avoids floating-point atan2, but integer arithmetic must be wide enough. Widen coordinates before subtraction; casting only after a narrow subtraction cannot repair overflow.

Duplicate points, fewer than three distinct points, and all-collinear data require explicit output rules. The complete class C++ source and its caveats remain available separately; these conditions are part of understanding the algorithm, not reasons to omit the code.

Tie-order clarification. The pseudocode keeps equal-angle points in increasing distance order, so the ≤0 pop removes each nearer point before retaining the farthest. This implements the earlier farthest-only rule without a separate filtering pass. With the lowest-leftmost pivot all other directions lie in one half-plane, so cross-product angle comparison with the stated distance tie-break is consistent. Empty, singleton and duplicate-only input is handled before choosing the pivot.

One stack row for every point in the notebook order

Read the stack below from bottom to top, left to right. This expands the source's crossed-out stack entries. The order is A, then B,J,D,E,G,F,C,H,I. The drawing supplies qualitative turns, not numerical coordinates, so no fabricated angle or determinant values are needed.

Incoming pointPop / keep actionStack after insertion
APush the pivot.A
BPush the next angular point.A,B
JThe turn A→B→J is accepted.A,B,J
DPush D provisionally.A,B,J,D
EPop D, retest using B,J,E, then push E.A,B,J,E
GPop E, retest using B,J,G, then push G.A,B,J,G
FPush F provisionally.A,B,J,G,F
CPop F, retest using J,G,C, then push C.A,B,J,G,C
HPop C, retest using J,G,H, then push H.A,B,J,G,H
IKeep the remaining convex turn and push I.A,B,J,G,H,I

“Provisional” is important: a point may form a valid turn when first inserted and later become internal. A push is not a final promise that the point lies on the completed hull. The notebook's final stack, popped top-first, prints I,H,G,J,B,A. Connecting those in reverse orientation closes the same polygon.

↗Compare original classroom note(s)5 source pages
Class notes · 14–16 September 2026page 1 of 16
Class notes · 14–16 September 2026page 2 of 16
Class notes · 14–16 September 2026page 3 of 16
Graham Scan to KMP, P, N, NP-type Algorithm.zipimage 1 of 12
Graham Scan to KMP, P, N, NP-type Algorithm.zipimage 2 of 12
Late-course classroom notes

Naïve String Matching

Nested comparison algorithm and O(nm) classroom complexity.

Teacher taughtHandwritten

The string-matching problem

Given a text T of length n and a pattern P of length m, find every starting index s for which T[s…s+m−1]=P[0…m−1]. Indices here are zero-based. A match starting at s ends at s+m−1. Matching is case-sensitive unless the input is deliberately normalized.

The naive method tries every possible alignment and compares corresponding characters from left to right. There are n−m+1 possible alignments when 1≤m≤n. A mismatch rejects the current alignment, not the entire search.

Complete algorithm

naiveMatch(T,P):
    n = length(T); m = length(P)
    if m == 0: return [0,1,...,n]  // explicitly chosen empty-pattern convention
    if m > n: return []
    matches = []
    for s = 0 to n-m inclusive:
        j = 0
        while j < m and T[s+j] == P[j]:
            j = j+1
        if j == m:
            append s to matches
    return matches

The test j<m must be evaluated before reading P[j]. The alignment limit s≤n−m guarantees s+j<n during each comparison. Moving the alignment by one also finds overlapping occurrences.

Worked alignment table

Using the same text and pattern as the later KMP example, T=ababcabcabababd and P=ababd, n=15 and m=5. The complete set of candidate windows is:

sFive-character windowOutcome
0ababcMismatch
1babcaMismatch
2abcabMismatch
3bcabcMismatch
4cabcaMismatch
5abcabMismatch
6bcabaMismatch
7cababMismatch
8ababaMismatch
9bababMismatch
10ababdMatch; indices 10…14

For s=0, a,b,a,b match and the fifth comparison c≠d fails. For s=10, all five comparisons succeed. The final answer is start index 10, end index 14. As a separate overlap example, T=aaaa and P=aa gives starts 0,1,2—not only 0 and 2.

Correctness and cost

Every possible occurrence has exactly one start s in 0…n−m. The outer loop visits all these starts. The inner loop accepts an alignment exactly when all m corresponding characters are equal. Therefore it reports every occurrence and no false occurrence.

At most m comparisons occur at each of n−m+1 alignments, so the worst-case comparison count is Θ((n−m+1)m), often written O(nm). An example is a text of all a's and a pattern a…ab, which fails only near the end of each window. If every window fails on its first comparison, the search takes Θ(n−m+1) comparisons. Working storage is O(1), excluding input and the list of k reported positions, which requires O(k).

Correction / clarification. The handwritten outer loop runs to i<n. That can access past the text when a multi-character pattern is compared near its end. Use i≤n−m. A character comparison uses ==, not assignment =. Empty patterns require a stated convention before indexing P[0]; the algorithm above uses all n+1 boundaries.

The handwritten counter-based version, corrected line by line

The class code uses a counter rather than a while-loop index. Both implement the same left-to-right comparisons. The counter is reset at each alignment; a mismatch breaks only the inner loop. A complete match prints both its start and inclusive end indices.

n = length(T); m = length(P)
if m == 0: report all boundaries 0..n; return
if m > n: return
for i = 0 to n-m inclusive:
    count = 0
    for j = 0 to m-1:
        if T[i+j] == P[j]:
            count = count+1
        else:
            break
    if count == m:
        print(i, i+m-1)
Correction / clarification. In the photo version, the print/check indentation appears inside the mismatch branch. It belongs after the inner loop: an all-matching window never enters the mismatch branch. Do not place a semicolon after the if condition in C++, and use i+m−1 for the end index; j declared inside a C++ for loop is out of scope afterward.
↗Compare original classroom note(s)2 source pages
Class notes · 14–16 September 2026page 4 of 16
Graham Scan to KMP, P, N, NP-type Algorithm.zipimage 4 of 12
Late-course classroom notes

Rabin–Karp String Matching

Hash values, rolling window, spurious hits, and verification.

Teacher taughtHandwritten

Why use a hash?

Rabin–Karp compares a numerical fingerprint of the pattern with fingerprints of equally long text windows. A rolling hash updates the next window from the previous one instead of recomputing every character contribution. Unequal hashes prove unequal strings. Equal hashes only identify a candidate: compare the characters to distinguish a real match from a collision (spurious hit).

The notebook's first hash: addition

The class example searches for FUR in a text beginning ASIFUR (the two versions continue RAHMAN and ISLAM). The demonstration assigns A=1, S=2, I=3, F=4, R=5, L=6, M=7, U=8. These are illustrative symbol codes, not actual ASCII values.

With the additive hash, H(FUR)=4+8+5=17. But H(RFU)=5+4+8=17 and H(FRU)=4+5+8=17 also. Thus a hash can ignore information—in this case order—and produce many spurious hits. For a length-m window, an additive hash rolls by subtracting the outgoing code and adding the incoming code.

The notebook's positional hash, fully worked

The second demonstration assigns increasing powers of 10 from left to right: H(c₀c₁…cₘ₋₁)=code(c₀)+10·code(c₁)+…+10ᵐ⁻¹·code(cₘ₋₁). For m=3:

WindowCalculationHash
ASI1+2·10+3·100321
SIF2+3·10+4·100432
IFU3+4·10+8·100843
FUR4+8·10+5·100584
nextHash = (currentHash - outgoingCode)/10 + incomingCode*10^(m-1)
ASI -> SIF: (321-1)/10 + 4*100 = 432
SIF -> IFU: (432-2)/10 + 8*100 = 843
IFU -> FUR: (843-3)/10 + 5*100 = 584

The division is exact here because the outgoing symbol was the units contribution. The target hash 584 matches the fourth window, starting at index 3. Comparing F,U,R confirms the occurrence. This small integer illustration is useful for understanding rolling updates; large windows require modular arithmetic to keep fingerprints bounded.

Practical modular version: a different, explicit power convention

For the usual implementation, use descending powers from left to right: H(c₀…cₘ₋₁)=(code(c₀)dᵐ⁻¹+…+code(cₘ₋₁)) mod q. Here d is the alphabet base and q is a positive modulus, normally chosen prime. Precompute h=dᵐ⁻¹ mod q. Removing the first character, shifting the remaining contributions left by one power, and adding the new character gives:

nextHash = (d*(currentHash - outgoingCode*h) + incomingCode) mod q
if nextHash < 0: nextHash += q

This formula does not divide the current hash. The notebook's ascending-power integer formula and this descending-power modular formula are each valid for their own definitions; mixing them is not valid.

Complete verified-match algorithm

rabinKarp(T,P,d,q):                 // q > 1
    n = length(T); m = length(P)
    if m == 0: return [0,1,...,n]
    if m > n: return []
    h = d^(m-1) mod q               // computed by modular exponentiation
    patternHash = 0; windowHash = 0
    for j = 0 to m-1:
        patternHash = (d*patternHash + code(P[j])) mod q
        windowHash  = (d*windowHash  + code(T[j])) mod q
    matches = []
    for s = 0 to n-m inclusive:
        if patternHash == windowHash:
            if T[s:s+m] equals P character by character:
                append s to matches
        if s < n-m:
            windowHash = (d*(windowHash-code(T[s])*h)+code(T[s+m])) mod q
            if windowHash < 0: windowHash += q
    return matches

Why it works; complexity

Initialization computes the fingerprint of the first length-m window. The rolling identity preserves that meaning at every shift. Every real occurrence therefore has the pattern hash and reaches the verification step. Verification accepts exactly equal strings, so collisions do not affect correctness.

Preprocessing takes O(m); there are O(n−m+1) constant-time hash updates under the word-arithmetic model. If H windows have matching hashes, verification costs at most O(Hm), making the total O(n+m+Hm). With few spurious hits and few occurrences, the usual expected cost is O(n+m). Worst case is O(nm), including collision-heavy inputs or many genuine length-m matches checked separately. Auxiliary hash state is O(1), excluding output.

Use sufficiently wide arithmetic or overflow-safe modular multiplication: applying %q after an overflowing signed product does not repair it. Normalize a negative remainder before comparing hashes. If character verification is omitted, hash collisions can be reported as false matches. With fixed d and q this is deterministic, not automatically a randomized algorithm. A Monte Carlo error bound additionally requires a specified randomized choice of hash parameters and its collision analysis. The character-verifying version above remains exact for every valid d and q.

Correction / clarification. The handwritten rolling pseudocode has ambiguous multiplication/power placement and an outer loop omitting the last valid window. The two fully defined formulas above replace that ambiguity. The class symbol-number mapping is a toy encoding, not ASCII; a hash match is not itself a string match.

Further clarification reference (optional; the explanation above is complete offline): Princeton Algorithms: randomized modulus and explicit match verification

The classroom ascending-power algorithm—not a different hash convention

Write PH for the pattern hash and TH for the current text-window hash. With base b and exact integer arithmetic, the notebook initializes PH=Σ code(P[j])bʲ and TH=Σ code(T[j])bʲ for j=0,…,m−1. The following completes that specific version, including the initial and last possible windows.

RABIN_KARP_ASCENDING(T,P,b):
    n = length(T); m = length(P)
    if m == 0: return [0,1,...,n]
    if m > n: return []
    PH = 0; TH = 0; power = 1
    for j = 0 to m-1:
        PH += code(P[j])*power
        TH += code(T[j])*power
        power *= b
    highestPower = power/b              // b^(m-1)
    matches = []
    for i = 0 to n-m inclusive:
        if PH == TH and T[i:i+m] equals P:
            append i to matches
        if i < n-m:
            TH = (TH-code(T[i]))/b + code(T[i+m])*highestPower
    return matches

The division is exact because subtracting the outgoing units digit leaves a multiple of b. The highest exponent remains m−1 after every slide; it does not grow with the window's starting index. PH stays fixed; TH must be updated and retained for the next iteration.

Correction / clarification. The handwritten update resembles a multiplication by pow(b,i+m−1), omits the incoming character contribution, and uses a loop bound i<n−m. The consistent update is (TH−code(T[i−1]))/b + code(T[i+m−1])·b^(m−1) when i denotes the new start, with i=1,…,n−m inclusive. The first window at i=0 must also be checked. The modular descending-power implementation above is a separate practical variant, not a literal transcription of this formula.

Complete coded-symbol example from the alternate notebook

The clearer symbol list reads A=1, S=2, I=3, F=4, R=5, L=6, M=7, U=8. The symbol with code 6 is L; no space code is supplied. For the coded letter sequence ASIFURISLAM and pattern FUR, the following table continues the source's sliding-window calculation through every legal window. This continuation is calculated from the supplied symbol codes; it is not a claim that every row was handwritten.

StartWindowAdditive calculationAdditive hashBase-10 positional hashCharacter check
0ASI1+2+36321Not a match
1SIF2+3+49432Not a match
2IFU3+4+815843Not a match
3FUR4+8+517584True match
4URI8+5+316358Not a match
5RIS5+3+210235Not a match
6ISL3+2+611623Not a match
7SLA2+6+19162Not a match
8LAM6+1+714716Not a match

The pattern has additive hash 17 and positional hash 584. At the final window LAM, the additive hash is 6+1+7=14, not 17, so it is rejected without a character check in the additive version. The occurrence FUR starts at 3 and ends at 5. If the actual input contains a separating space, give that symbol a consistent code and include it in the windows; do not silently remove it.

Why the base matters

The notes briefly use base 17 before working the shifts in base 10. For FUR, base 17 gives 4+8·17+5·17²=1585; base 10 gives 584. RFU gives 5+4·10+8·100=845 and FRU gives 4+5·10+8·100=854, so positional weighting distinguishes those additive collisions. With unbounded exact integers and digit codes smaller than b, fixed-length positional encoding is injective. A bounded modular fingerprint can still collide. Exact integers may become large, so treating all arithmetic as constant-time is only an illustrative classroom model.

↗Compare original classroom note(s)8 source pages
Class notes · 14–16 September 2026page 5 of 16
Class notes · 14–16 September 2026page 6 of 16
Class notes · 14–16 September 2026page 7 of 16
Class notes · String matching · 15 September 2026page 1 of 7
Class notes · String matching · 15 September 2026page 2 of 7
Class notes · String matching · 15 September 2026page 3 of 7
Graham Scan to KMP, P, N, NP-type Algorithm.zipimage 3 of 12
Graham Scan to KMP, P, N, NP-type Algorithm.zipimage 5 of 12
Late-course classroom notes

KMP & LPS

Manual LPS construction, mismatch fallback, and the main pattern-search trace.

Teacher taughtHandwritten

What repeated work does KMP remove?

After a partial match followed by a mismatch, naive search starts the next alignment from scratch. Knuth–Morris–Pratt (KMP) uses information about the matched prefix to retain a suffix that is already known to match the beginning of the pattern. The text index never moves backward.

Prefix, suffix, and LPS

A prefix starts at the first character; a suffix ends at the last. A proper prefix is shorter than the whole string. LPS[i] is the length of the longest proper prefix of P[0…i] that is also a suffix of P[0…i]. The empty string ε has length zero and is allowed when there is no nonempty border.

For the classroom pattern abaabad, the full prefix/suffix calculation is:

iP[0…i]Nonempty proper prefixesNonempty proper suffixesLongest common borderLPS[i]
0a∅∅ε0
1ababε0
2abaa, aba, baa1
3abaaa, ab, abaa, aa, baaa1
4abaaba, ab, aba, abaab, ab, aab, baabab2
5abaabaa, ab, aba, abaa, abaaba, ba, aba, aaba, baabaaba3
6abaabada, ab, aba, abaa, abaab, abaabad, ad, bad, abad, aabad, baabadε0

The resulting array is [0,0,1,1,2,3,0]. For example, abaaba begins and ends with aba, so LPS[5]=3. The whole six-character string is excluded because the prefix must be proper.

Computing LPS in linear time

buildLPS(P):                     // nonempty P
    m = length(P)
    LPS[0] = 0
    length = 0; i = 1
    while i < m:
        if P[i] == P[length]:
            length = length+1
            LPS[i] = length
            i = i+1
        else if length > 0:
            length = LPS[length-1]   // do not advance i
        else:
            LPS[i] = 0
            i = i+1
    return LPS

Before processing i, length is a candidate border of P[0…i−1]. If P[i] continues it, the border grows by one. Otherwise the next possible border must itself be a border of that candidate prefix, so the fallback is LPS[length−1]. Subtracting one blindly misses the structural reason for skipping candidates.

Two further classroom LPS examples

For aaacaaaa, LPS=[0,1,2,0,1,2,3,3]. At the last character, length=3 first compares a with P[3]=c and fails. Falling back to LPS[2]=2 allows a match with P[2]=a; the final entry becomes 3, not 4 and not 0.

For the longer pattern acacabacacabacacac, every index and final value is shown below, followed by the complete fallback trace.

IndexCharacterLPS
0a0
1c0
2a1
3c2
4a3
5b0
6a1
7c2
8a3
9c4
10a5
11b6
12a7
13c8
14a9
15c10
16a11
17c4
iP[i]Candidate length before testResult
1c0Mismatch at length 0; LPS[1]=0; advance i
2a0P[2] = P[0]; LPS[2]=1; advance i
3c1P[3] = P[1]; LPS[3]=2; advance i
4a2P[4] = P[2]; LPS[4]=3; advance i
5b3Mismatch; fall back to LPS[2]=1; keep i
5b1Mismatch; fall back to LPS[0]=0; keep i
5b0Mismatch at length 0; LPS[5]=0; advance i
6a0P[6] = P[0]; LPS[6]=1; advance i
7c1P[7] = P[1]; LPS[7]=2; advance i
8a2P[8] = P[2]; LPS[8]=3; advance i
9c3P[9] = P[3]; LPS[9]=4; advance i
10a4P[10] = P[4]; LPS[10]=5; advance i
11b5P[11] = P[5]; LPS[11]=6; advance i
12a6P[12] = P[6]; LPS[12]=7; advance i
13c7P[13] = P[7]; LPS[13]=8; advance i
14a8P[14] = P[8]; LPS[14]=9; advance i
15c9P[15] = P[9]; LPS[15]=10; advance i
16a10P[16] = P[10]; LPS[16]=11; advance i
17c11Mismatch; fall back to LPS[10]=5; keep i
17c5Mismatch; fall back to LPS[4]=3; keep i
17c3P[17] = P[3]; LPS[17]=4; advance i

At i=17, the candidates fall from 11 to LPS[10]=5, then to LPS[4]=3. P[3]=c matches the new c, so LPS[17]=4. The repeated mismatch does not consume the new character until a valid border continuation or length-zero failure is found.

The KMP matching algorithm

KMP(T,P):
    n = length(T); m = length(P)
    if m == 0: return [0,1,...,n]
    LPS = buildLPS(P)
    i = 0; j = 0; matches = []
    while i < n:
        if T[i] == P[j]:
            i = i+1; j = j+1
            if j == m:
                append i-m to matches
                j = LPS[m-1]     // preserve possible overlapping matches
        else if j > 0:
            j = LPS[j-1]         // same text character, shorter known prefix
        else:
            i = i+1
    return matches

Complete classroom search trace

Text T=ababcabcabababd; pattern P=ababd; LPS=[0,0,1,2,0]. Each row is one character comparison; i and j are the values before that comparison.

ijT[i]P[j]Action
00aaMatch → i=1, j=1
11bbMatch → i=2, j=2
22aaMatch → i=3, j=3
33bbMatch → i=4, j=4
44cdMismatch → j=LPS[3]=2; keep i=4
42caMismatch → j=LPS[1]=0; keep i=4
40caMismatch at j=0 → i=5
50aaMatch → i=6, j=1
61bbMatch → i=7, j=2
72caMismatch → j=LPS[1]=0; keep i=7
70caMismatch at j=0 → i=8
80aaMatch → i=9, j=1
91bbMatch → i=10, j=2
102aaMatch → i=11, j=3
113bbMatch → i=12, j=4
124adMismatch → j=LPS[3]=2; keep i=12
122aaMatch → i=13, j=3
133bbMatch → i=14, j=4
144ddMatch → i=15, j=5; report start 10; reset j=LPS[4]=0

The only occurrence starts at index 10 and ends at 14. At i=12, j=4, the mismatch a≠d does not advance i: j falls to 2, so the same a can continue the shorter border ab. This is the central saving over restarting from the beginning.

Why fallback cannot skip a valid match

When j characters have matched, the already matched text segment equals P[0…j−1]. Any next alignment that reuses part of this segment must make a suffix of that segment equal a prefix of P. LPS[j−1] gives the longest such candidate. If it fails too, following the LPS chain considers the next shorter border. Alignments not represented by a border cannot match the characters already examined, so skipping them is safe.

Complexity and boundary cases

LPS preprocessing is O(m): successful extensions advance i and increase the border length; every fallback strictly decreases that length, so the total number of fallbacks is bounded by prior increases. The same amortized argument gives O(n) matching work. Total time is O(n+m); LPS storage is O(m), plus O(k) output positions. If m>n there are no occurrences. After a full match, resetting j to LPS[m−1], not always zero, preserves overlapping matches.

Correction / clarification. The handwritten shorthand “length−1” must not be read as the fallback assignment length=length−1. The correct assignment is length=LPS[length−1]. The search fallback similarly uses j=LPS[j−1] while keeping i fixed. The complete tables above use these corrected rules.

The two small border drawings

For abba, the proper prefixes are a,ab,abb and the proper suffixes are a,ba,bba. Their longest common member is a, length 1. For abab, the proper prefixes are a,ab,aba and the proper suffixes are b,ab,bab. Their longest common member is ab, length 2. These are the small “1” and “2” drawings beneath the first LPS array.

Every preprocessing step for aaacaaaa

This is the short repeated-letter example used to introduce the fallback code. Each row records the state before one comparison. In particular, the c at index 3 must be compared against candidates of lengths 2,1,0; i stays fixed during the two nonzero fallbacks.

iP[i]length beforeP[length]Update
1a0aMatch; LPS[1]=1; advance i to 2
2a1aMatch; LPS[2]=2; advance i to 3
3c2aMismatch; length=LPS[1]=1; keep i=3
3c1aMismatch; length=LPS[0]=0; keep i=3
3c0aMismatch at length 0; LPS[3]=0; advance i to 4
4a0aMatch; LPS[4]=1; advance i to 5
5a1aMatch; LPS[5]=2; advance i to 6
6a2aMatch; LPS[6]=3; advance i to 7
7a3cMismatch; length=LPS[2]=2; keep i=7
7a2aMatch; LPS[7]=3; advance i to 8

Build ababd's LPS before running the search

The photo notebook includes this preprocessing trace separately from the text-search trace. LPS[0]=0. The remaining comparisons are:

iP[i]length beforeP[length]Update
1b0aMismatch at length 0; LPS[1]=0; advance i to 2
2a0aMatch; LPS[2]=1; advance i to 3
3b1bMatch; LPS[3]=2; advance i to 4
4d2aMismatch; length=LPS[1]=0; keep i=4
4d0aMismatch at length 0; LPS[4]=0; advance i to 5
Correction / clarification. In the photo notebook, the ababd preprocessing mismatch at i=4 is followed by a trial length 1. The actual LPS fallback is 2→LPS[1]=0, skipping 1 because it is not a border of “ab”. The complete result remains [0,0,1,2,0]. Likewise, the longer-pattern source sometimes lists length 2 between 3 and 1 at i=5; the valid border chain is 3→1→0. The main trace above follows that chain.

The search table is a different trace: there i indexes T and j indexes P. In preprocessing, i and length both index P. Mixing these two roles is a common reason for producing a plausible-looking but incorrect trace.

↗Compare original classroom note(s)18 source pages
Class notes · 14–16 September 2026page 8 of 16
Class notes · 14–16 September 2026page 9 of 16
Class notes · 14–16 September 2026page 10 of 16
Class notes · 14–16 September 2026page 11 of 16
Class notes · 14–16 September 2026page 12 of 16
Class notes · 14–16 September 2026page 13 of 16
Class notes · 14–16 September 2026page 14 of 16
Class notes · 14–16 September 2026page 15 of 16
Class notes · String matching · 15 September 2026page 4 of 7
Class notes · String matching · 15 September 2026page 5 of 7
Class notes · String matching · 15 September 2026page 6 of 7
Class notes · String matching · 15 September 2026page 7 of 7
Graham Scan to KMP, P, N, NP-type Algorithm.zipimage 6 of 12
Graham Scan to KMP, P, N, NP-type Algorithm.zipimage 7 of 12
Graham Scan to KMP, P, N, NP-type Algorithm.zipimage 8 of 12
Graham Scan to KMP, P, N, NP-type Algorithm.zipimage 9 of 12
Graham Scan to KMP, P, N, NP-type Algorithm.zipimage 10 of 12
Graham Scan to KMP, P, N, NP-type Algorithm.zipimage 12 of 12
Late-course classroom notes

P, NP, NP-hard & NP-complete

Teacher introduced the classes with Venn-style diagrams and a reduction heading.

Teacher taughtHandwritten

Start with decision problems and input size

A decision problem asks a yes/no question: “Does this graph contain a path from s to t?” or “Is there a vertex cover of size at most k?” Complexity is measured in the length of the encoded input, not only the numerical value of a parameter. Polynomial time means a bound O(Nᶜ) for a fixed constant c and input length N.

An algorithm is a procedure for solving a problem. A complexity class is a set of problems meeting a resource bound. Sorting algorithms such as merge sort illustrate polynomial computation, but P and NP are conventionally defined for decision problems, so algorithm names alone are not formal members of these classes.

P: efficiently solvable decision problems

P is the set of decision problems solvable by a deterministic algorithm in polynomial time. Examples include reachability (run BFS/DFS and answer whether t is reached), and deciding whether a weighted undirected graph has a spanning tree of total weight at most K (compute an MST and compare). Deterministic means that the current state and input determine the next step.

NP: efficiently verifiable yes answers

NP is the set of decision problems with a deterministic polynomial-time verifier and a polynomial bound on certificate length: every yes-instance has at least one accepted certificate, and a no-instance has no accepted certificate. Both conditions are essential. A procedure that accepts every purported certificate is not a valid verifier for a problem that has no-instances. Equivalently, they are solvable by a nondeterministic machine in polynomial time. Nondeterminism is a theoretical ability to branch among possible choices; it does not mean randomization.

For a Hamiltonian-cycle decision problem, a certificate is an ordering of all vertices. Check that every vertex occurs once and that every consecutive pair, including last-to-first, is an edge. For vertex cover, a certificate is a set S of at most k vertices; check that every edge has an endpoint in S. These checks are polynomial even if we do not know how to find such a certificate efficiently for every input.

NP guarantees efficient verification of yes certificates. It does not assert that every no-instance has an equally short, efficiently checkable certificate. Also, every P problem lies in NP: a verifier can solve the problem itself without needing a helpful certificate.

Polynomial-time reduction

A many-one reduction A≤ₚB transforms every input x of A, in polynomial time, into an input f(x) of B such that x is a yes-instance of A if and only if f(x) is a yes-instance of B. An algorithm for B can then solve A by transforming the input and solving the transformed instance.

To solve A using B:
    y = f(x)              // polynomial-time transformation
    return solver_B(y)    // preserves the yes/no answer

To prove a new problem X is hard:
    take a known hard problem Y
    prove Y <=p X         // reduce known-hard TO the new target, not backward

The direction matters: A≤ₚB says B is at least as hard as A in this reduction sense. Showing X≤ₚY for a known hard Y only shows that Y can solve X; it does not establish hardness of X. Polynomial reductions compose, which allows hardness to be transferred through a chain.

NP-hard and NP-complete

ClassPrecise conditionImportant consequence
PDecision problem solvable deterministically in polynomial timeP⊆NP
NPYes-certificates have polynomial length and polynomial-time verificationDoes not mean “non-polynomial”
NP-hardEvery problem in NP reduces to it in polynomial timeNeed not itself belong to NP; may be an optimization problem
NP-completeBoth in NP and NP-hardAmong the hardest decision problems in NP

To prove X is NP-complete, establish both parts: (1) X∈NP by describing a certificate, its size, and its polynomial verifier; (2) NP-hardness by reducing an already known NP-complete problem Y to X with a polynomial transformation and an if-and-only-if correctness proof.

Examples and the distinction between decision and optimization

  • SAT decision: is there a truth assignment satisfying a Boolean formula? An assignment is a polynomially verifiable certificate; SAT is NP-complete.
  • Hamiltonian-cycle decision: does the graph contain a cycle visiting each vertex exactly once? NP-complete for general graphs.
  • Vertex-cover decision: does a general graph have a vertex cover of size ≤k? NP-complete. Finding a smallest cover is the related NP-hard optimization problem.
  • Traveling salesperson: the decision version asks whether a tour of cost ≤K exists; with standard finite input encoding it is NP-complete. Finding a minimum-cost tour is the NP-hard optimization version.

Restrictions can change complexity: for example, bipartite graphs admit polynomial-time minimum vertex cover. Therefore a classification must state the actual problem and graph restrictions, not just a familiar topic name.

Reading the relationship diagram correctly

P⊆NP, and NP-complete=NP∩NP-hard. Whether P=NP is unresolved. If any NP-complete problem has a polynomial-time algorithm, then every NP problem does and P=NP. A familiar diagram drawing P as a smaller region inside NP is an illustration of the hypothesis P≠NP, not a proved separation. NP-hard problems need not be inside NP.

Correction / clarification. The notes associate NP with exponential solving time and NP-hard with exponential verification. Those are not definitions. NP means nondeterministic polynomial time / polynomial verification, not “not polynomial.” No proof is known that all NP-complete problems require exponential time, and NP-hardness alone does not specify a verification-time bound.

Further clarification reference (optional; the explanation above is complete offline): Cornell complexity notes: the if-and-only-if verifier definition

↗Compare original classroom note(s)3 source pages
Class notes · 14–16 September 2026page 15 of 16
Class notes · 14–16 September 2026page 16 of 16
Graham Scan to KMP, P, N, NP-type Algorithm.zipimage 11 of 12
Late-course classroom notes

Dijkstra & Prim — worked class sheets

Two supplied typed pages give step-by-step tables and final graphs.

Teacher/topic materialWorked example

Dijkstra: shortest paths from one source

Given a directed or undirected graph with nonnegative edge weights and source s, Dijkstra computes the minimum path weight from s to every reachable vertex. Set dist[s]=0 and all other distances to ∞. Repeatedly settle an unsettled vertex with the smallest tentative distance, then relax its outgoing edges.

Relaxing u→v of weight w means testing whether dist[u]+w<dist[v]. If so, replace dist[v] by that smaller value and set parent[v]=u. Parent pointers reconstruct a shortest path; unreachable vertices keep distance ∞ and no parent.

Dijkstra(G,s):
    dist[v] = infinity and parent[v] = none for every v
    dist[s] = 0
    minHeap.push((0,s))
    while minHeap is not empty:
        (du,u) = minHeap.popMin()
        if du != dist[u]: continue       // stale entry from an earlier improvement
        for every outgoing edge (u,v,w):
            if dist[u]+w < dist[v]:
                dist[v] = dist[u]+w
                parent[v] = u
                minHeap.push((dist[v],v))

Full classroom directed graph

The following edge list is the readable equivalent of the diagram. An arrow is directed; a reverse edge exists only if it is separately listed.

FromToWeight
su10
sx5
uv1
ux2
xu3
xv9
xy2
vy4
yv6
ys7

Every settlement and distance update

Settled vertexdist(s)dist(u)dist(x)dist(y)dist(v)Changes made
Initial0∞∞∞∞Only source initialized
s (0)0105∞∞u←s, x←s
x (5)085714u←x (5+3); y←x (5+2); v←x (5+9)
y (7)085713v←y (7+6); edge to s gives 14, no improvement
u (8)08579v←u (8+1); edge to x gives 10, no improvement
v (9)08579edge to y gives 13, no improvement

Settlement order is s,x,y,u,v. The heap can still contain old entries u(10), v(13), and v(14); these are discarded as stale, not treated as new distances.

VertexFinal distanceParentA shortest path
s0—s
x5ss→x
y7xs→x→y
u8xs→x→u
v9us→x→u→v

Why the smallest tentative distance is safe

Suppose u is the unsettled vertex with smallest tentative distance, but a shorter path to u exists. On that path, consider the first unsettled vertex y and its settled predecessor x. When x was settled, relaxation made dist[y] no greater than the length of the path prefix to y. Nonnegative remaining edges mean that prefix is no longer than the full supposedly shorter path to u. This contradicts u having the minimum tentative distance. Therefore dist[u] is final when settled.

A negative edge breaks this argument: a path through a vertex with a currently larger distance may later become cheaper. Use an appropriate algorithm such as Bellman–Ford when negative weights must be supported; do not claim the Dijkstra guarantee there.

Prim: minimum spanning tree, not shortest paths

For a connected, undirected weighted graph, a spanning tree connects all vertices without a cycle using V−1 edges. An MST minimizes the sum of its edge weights. Prim starts with one vertex and repeatedly adds the cheapest edge crossing from the current tree to an outside vertex.

For each outside vertex v, key[v] is the lightest single edge connecting it to the current tree; parent[v] is the endpoint inside the tree. It is not the distance along a path from the starting vertex.

Prim(G,start):
    key[v] = infinity; parent[v] = none; inTree[v] = false for every v
    key[start] = 0
    minHeap.push((0,start))
    while minHeap is not empty:
        (k,u) = minHeap.popMin()
        if inTree[u] or k != key[u]: continue
        inTree[u] = true
        for every undirected incident edge (u,v,w):
            if not inTree[v] and w < key[v]:
                key[v] = w               // NOT key[u]+w
                parent[v] = u
                minHeap.push((key[v],v))
    if any vertex is outside the tree: graph is disconnected
    otherwise return all edges (parent[v],v) for v != start

Full classroom undirected graph

EndpointEndpointWeight
HA6
AB4
HB5
BC9
BE2
ED15
EF8
FG3
GH14
HF10

Complete Prim trace, starting at G

Added vertexChosen connecting edgeNew or improved frontier keysRunning tree weight
GStart; no edgeF=3 via G; H=14 via G0
FG–F (3)E=8 via F; H improves to 10 via F3
EF–E (8)B=2 via E; D=15 via E11
BE–B (2)A=4 via B; H improves to 5 via B; C=9 via B13
AB–A (4)A–H=6 does not improve H=517
HB–H (5)No new outside vertex22
CB–C (9)No improvement31
DE–D (15)All eight vertices included46
VertexFinal key (connecting-edge weight)Parent
A4B
B2E
C9B
D15E
E8F
F3G
G0—
H5B

The MST contains GF, FE, EB, BA, BH, BC, ED, with total weight 3+8+2+4+5+9+15=46. Unlike Dijkstra distances, the sequence of selected edge weights need not increase: selecting E with key 8 reveals B with key 2.

Cut-property justification

Let S be the current tree vertices. A minimum-weight edge crossing the cut (S,V−S) is safe: take an MST containing the already selected edges. If it does not contain this crossing edge, adding it creates a cycle. The cycle contains another edge crossing the same cut. Replacing that edge by the chosen minimum crossing edge does not increase the weight, so an MST still exists containing all selected edges. Repeating the argument proves Prim's result.

Comparison, complexity, and boundary cases

AspectDijkstraPrim
GoalMinimize each source-to-vertex path weightMinimize total spanning-tree edge weight
Updatedist[v] > dist[u]+wkey[v] > w
Meaning of priorityTentative path distanceCheapest edge joining the current tree
Graph restrictionNonnegative weights; directed or undirectedUndirected; negative weights allowed
Disconnected inputUnreachable distances remain ∞No spanning tree; restarting per component gives a minimum spanning forest
Dense matrix implementationO(V²) timeO(V²) time
Adjacency list + indexed binary heapO((V+E) log V) time; O(V+E) total storageO((V+E) log V) time; O(V+E) total storage

The shown lazy heaps can contain O(E) entries, giving O((V+E) log(V+E)) time in general and O(V+E) storage; for simple graphs this is customarily written O((V+E) log V). Tie-breaking may choose different valid parents or different MSTs with the same total weight. A shortest-path tree is not necessarily an MST, and an MST need not contain shortest source-to-vertex paths.

Correction / clarification. In the typed Prim final table, D has its key and parent interchanged: the correct key is 15 and parent E. One intermediate row prints the B–H candidate as 4; the graph and final table give 5. Dijkstra’s leftover queue entries are old proposals, not additional final results.

The worksheet's candidate queues, fully transcribed

The earlier distance table records the best known value per vertex. The worksheet instead keeps candidate entries, including obsolete entries. Write an entry as from→to(value). For Dijkstra, value is the candidate's whole source-to-destination distance, not just that edge's weight.

After processingCandidate entries shown, smallest value firstNext useful pop
ss→x(5), s→u(10)x at distance 5
xx→y(7), x→u(8), s→u(10), x→v(14)y at distance 7
yx→u(8), s→u(10), y→v(13), x→v(14)u at distance 8
uu→v(9), s→u(10), y→v(13), x→v(14)v at distance 9
vs→u(10), y→v(13), x→v(14)All stale; discard, then finish

For example, x→v(14) means dist(x)+w(x,v)=5+9=14. Once u provides 8+1=9, the old 14 entry must not overwrite dist(v)=9. The final shortest-path-tree edges are s→x(5), x→u(3), x→y(2), u→v(1); edge labels and destination distances are different quantities.

Prim's candidate-edge queues from the second sheet

For Prim, the number in each entry is only the connecting edge weight. Obsolete entries may stay in a lazy heap until their destination is already in the tree. The table follows the worksheet's progressively cleaned frontier; omitting a stale entry earlier or discarding it later produces the same selected edges.

After processingRemaining candidate edges in the sheetNext selected edge
GG–F(3), G–H(14)G–F(3)
FF–E(8), F–H(10), G–H(14)F–E(8)
EE–B(2), F–H(10), E–D(15); old G–H(14) may still be storedE–B(2)
BB–A(4), B–H(5), B–C(9), F–H(10), E–D(15)B–A(4)
AB–H(5), B–C(9), E–D(15); stale H candidates need not be retainedB–H(5)
HB–C(9), E–D(15)B–C(9)
CE–D(15)E–D(15)
DNo outside vertex remainsStop

The sheet's step-5 B–H value printed as 4 is corrected to 5, consistently with the graph and final parent table. A–H has weight 6 and cannot improve H's current key 5. The final red-arrow picture represents parent connections of an undirected spanning tree, not directed-only edges of the input graph.

↗Compare original classroom note(s)2 source pages
Worked examples · Dijkstra and Primpage 1 of 2
Worked examples · Dijkstra and Primpage 2 of 2
Implementation reference

Complete class C++ library

Full source listings and implementation-specific caveats; the digital lessons above explain the theory.

Restored code coverage · 19 current + 2 additional unique listings

Complete C++ code index

19 current class implementations, each complete. Every card has a variable/state guide, execution steps, complexity, boundary cases, an observed sample output, conceptual checks, and copy/download controls. Listings are independent programs; do not paste all their main functions into one compilation unit.

Geometry codeKMP code
AlgorithmGroupClass filenameTime
Graham Scan · NEW FILENew class code · GeometryGeometry/graham scan geo.cppO(n log n): sorting dominates; stack scan O(n).
Jarvis March / Gift Wrapping · NEW FILENew class code · GeometryGeometry/jarvis geo algo.cppO(nh); worst case O(n²), with h hull vertices.
KMP and LPS · NEW FILENew class code · StringString/kmp.cppO(n+m) time, including O(m) preprocessing.
Merge SortDivide and conquerConquer/merge_sort.cppΘ(n log n) best/average/worst.
Divide-and-Conquer Min–MaxDivide and conquerConquer/min_max.cppΘ(n); for n a power of two (n≥2), 3n/2−2 comparisons.
Quicksort · First PivotDivide and conquerConquer/quick_sort.cppAverage Θ(n log n); worst Θ(n²), including sorted/reverse-sorted/all-equal adversarial inputs.
Quicksort · Pivot ChoicesDivide and conquerConquer/quick_sort_pivot_choice.cppWorst Θ(n²) for every choice; randomized expected Θ(n log n) under suitable distinct-key inputs.
Fractional KnapsackGreedyGreedy/fractional knapsack.cppO(n log n) sorting + O(n) selection.
Job Sequencing with DeadlinesGreedyGreedy/job sequencing.cppO(n log n+nD+D) for this backward-slot-scan implementation.
Dijkstra · Adjacency List + HeapGreedy · GraphsGreedy/dijkstra.cppO(V+E log(E+1)) with lazy heap entries; commonly O((V+E) log V) for simple graphs.
Prim · Minimum Spanning TreeGreedy · GraphsGreedy/prims.cppO(V+E log(E+1)) for this lazy heap implementation; commonly O((V+E) log V) for simple graphs.
0–1 Knapsack · DP + ReconstructionDynamic programmingDynamic Programming/0_1 knapsack.cppO(nW), pseudo-polynomial in numeric capacity W.
Coin Change · Minimum CoinsDynamic programmingDynamic Programming/coin change.cppO(nA), A=amount; reconstruction O(n+A/cmin) for positive minimum coin cmin.
Longest Common SubsequenceDynamic programmingDynamic Programming/LCS.cppO(nm); backtracking O(n+m).
Subset Sum · Boolean DPDynamic programmingDynamic Programming/SOS.cppO(nT), T=target; pseudo-polynomial.
Sum of Subsets · BacktrackingBacktrackingBacktracking/sos.cppO(2ⁿ) search nodes; O(n·2ⁿ) conservative bound including printing.
N-Queens · All SolutionsBacktrackingBacktracking/n queen.cppExponential; O(n²·n!) is a safe bound for this full-column-loop + O(n) safety-check version, plus solution output.
M-Coloring · All AssignmentsBacktrackingBacktracking/m coloring.cppO(n·mⁿ) worst case for m≥2 with linear safety scans; O(n²) scanning when m=1 reaches every level.
Edmonds–Karp · Residual MatrixNetwork flowFlow Network/edmond karp.cppStandard adjacency-list Edmonds–Karp: O(VE²). This matrix-scanning version: O(V³E) via O(VE) augmentations × O(V²) BFS.

Additional references: historical fractional-knapsack variant (known percentage-printing bug) and complete quicksort experiment program (not presented as teacher lab code).

Compile each class program separately, e.g. g++ -std=c++17 -O2 "kmp.cpp" -o kmp. The experiment program requires C++20. The browser displays/downloads C++; it does not compile or execute it.

New class code · Geometry

Graham Scan

COMPLETE CLASSROOM IMPLEMENTATION

Find the extreme vertices of a planar point set. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: p0 = lowest (y, then x); S = candidate hull in counter-clockwise order.

Read the code in this order

  1. Choose the lowest point and sort the others by polar angle; equal angles put nearer points first.
  2. Pop b, inspect a = new top, and test turn(a,b,c). A strict left turn restores b and pushes c.
  3. On a right turn or collinearity, discard b and retry the SAME c. If one point remains, push c.
  4. Reverse the popped stack to restore traversal order. Each point is pushed/popped only a constant number of times.
Time — this implementationAuxiliary space / storage
O(n log n): sorting dominates; stack scan O(n).O(n) auxiliary: copied points, stack and returned hull.

Assumptions, pitfalls & corrections

  • turn returns +1 for CCW, −1 for CW, 0 for collinear. The code uses (b−a)×(c−b), equal to (b−a)×(c−a).
  • Collinear interior boundary points are discarded, not all printed. All-collinear distinct points reduce to two endpoints.
  • Input is hard-coded in main; n<3 returns the supplied points. Duplicate coordinates are not deduplicated.
  • 1LL widens multiplication, but subtraction occurs as int first; extreme coordinates can overflow before the cast. Cast operands before subtracting and choose a sufficiently wide product type.
Checked sample input and observed output

No stdin: the sample data are hard-coded in main.

Observed stdout · GNU C++17 build

Sorted Points:
(0, 0)
(4, 0)
(5, 2)
(2, 1)
(3, 2)
(2, 2)
(3, 3)
(4, 4)
(2, 3)
(1, 2)
(2, 5)
(0, 4)

Convex Hull Points:
(0, 0)
(4, 0)
(5, 2)
(4, 4)
(2, 5)
(0, 4)

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 197 lines
#include <bits/stdc++.h>
using namespace std;

struct Point {
    int x, y;
};

Point p0;

int turn(Point a, Point b, Point c) {

    long long cross =
        1LL * (b.x - a.x) * (c.y - b.y)
        - 1LL * (b.y - a.y) * (c.x - b.x);

    if (cross > 0)
        return 1;       // counter-clockwise turn

    if (cross < 0)
        return -1;      // clockwise turn

    return 0;           // collinear
}


int polar_angle(Point a, Point b, Point c) {

    long long area =
        1LL * (b.x - a.x) * (c.y - a.y)
        - 1LL * (b.y - a.y) * (c.x - a.x);

    if (area > 0)
        return 1;

    if (area < 0)
        return -1;

    return 0;
}

long long distanceSq(Point a, Point b) {

    return 1LL * (a.x - b.x) * (a.x - b.x)
         + 1LL * (a.y - b.y) * (a.y - b.y);
}

bool compare(Point a, Point b) {

    int result = polar_angle(p0, a, b);

    if (result == 1)
        return true;

    if (result == -1)
        return false;

    // Same angle → closer point first
    return distanceSq(p0, a) < distanceSq(p0, b);
}

vector<Point> GrahamScan(vector<Point> points) {

    int n = points.size();

    if (n < 3)
        return points;

    int lowest = 0;

    for (int i = 1; i < n; i++) {

        if (points[i].y < points[lowest].y ||
            (points[i].y == points[lowest].y &&
             points[i].x < points[lowest].x)) {

            lowest = i;
        }
    }

    swap(points[0], points[lowest]);

    p0 = points[0];

    sort(points.begin() + 1, points.end(), compare);

    cout << "\nSorted Points:\n";

    for (Point p : points) {
        cout << "(" << p.x << ", " << p.y << ")\n";
    }

    stack<Point> S;

    S.push(points[0]);   // a
    S.push(points[1]);   // b


    for (int i = 2; i < n; i++) {

        Point c = points[i];

        // Keep checking the SAME c until
        // it can be safely pushed
        while (S.size() >= 2) {

            // ----------------------------------
            // b = top element
            // ----------------------------------
            Point b = S.top();
            S.pop();

            // ----------------------------------
            // a = second top element
            // ----------------------------------
            Point a = S.top();

            // ----------------------------------
            // Check whether ab -> bc
            // makes a counter-clockwise turn
            // ----------------------------------
            int result = turn(a, b, c);


            if (result == 1) {

                S.push(b);
                S.push(c);

                break;
            }
            else {

                // Clockwise or collinear
                // b is NOT part of convex hull

                // b has already been popped.
                // Now evaluate the SAME c
                // with the new top two points.

                continue;
            }
        }

        // If only one point remains,
        // push c
        if (S.size() == 1) {
            S.push(c);
        }
    }

    vector<Point> hull;

    while (!S.empty()) {

        hull.push_back(S.top());
        S.pop();
    }

    reverse(hull.begin(), hull.end());

    return hull;
}


int main() {

    vector<Point> points = {

        {0, 0},
        {2, 1},
        {4, 0},
        {5, 2},
        {4, 4},
        {2, 5},
        {0, 4},
        {1, 2},

        // Interior points
        {2, 2},
        {3, 2},
        {2, 3},
        {3, 3}
    };

    vector<Point> hull = GrahamScan(points);

    cout << "\nConvex Hull Points:\n";

    for (Point p : hull) {

        cout << "("
             << p.x << ", "
             << p.y << ")\n";
    }

    return 0;
}

Check your understanding

Why does a popped point never need to return?

It lies on a non-left turn of the candidate boundary and cannot remain an extreme vertex in this angular sweep.

Why sort equal angles by distance?

Nearer points appear first; the non-left-turn pop removes them in favor of the farthest endpoint.

Why retry the same point c after a pop?

Removing one bad turn can expose another; c must form a valid left turn with the new top two points before it is settled.

↑ Back to code index · Choose / compare algorithms

New class code · Geometry

Jarvis March / Gift Wrapping

COMPLETE CLASSROOM IMPLEMENTATION

Wrap the outer boundary, selecting one hull vertex per full scan. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: p = current hull vertex; q = best next endpoint; h = hull vertex count.

Read the code in this order

  1. Start at the leftmost point (lowest y breaks ties).
  2. Initialize q=(p+1)%n; inspect every point i.
  3. Replace q when CCW(p,q,i)==1; for collinearity choose the farther point.
  4. Append the current vertex and advance to q until returning to the start.
Time — this implementationAuxiliary space / storage
O(nh); worst case O(n²), with h hull vertices.O(n) in this exact function because points is passed by value; O(h) hull storage.

Assumptions, pitfalls & corrections

  • Do not infer output direction from the comment alone: this replacement sign prints the supplied sample clockwise, while Graham prints it counter-clockwise.
  • Farthest-on-collinearity avoids selecting an interior point on the same ray.
  • For n<3 the program prints “Convex hull cannot be formed”; mathematically a point or segment is still a degenerate convex hull.
  • Deduplicate coordinates before general-purpose use. Extreme-coordinate subtraction/product overflow has the same risk as Graham Scan.
Checked sample input and observed output

No stdin: the sample data are hard-coded in main.

Observed stdout · GNU C++17 build

Input Points:
(0, 0)
(2, 1)
(4, 0)
(5, 2)
(4, 4)
(2, 5)
(0, 4)
(1, 2)
(2, 2)
(3, 2)
(2, 3)
(3, 3)

Convex Hull Points:
(0, 0)
(0, 4)
(2, 5)
(4, 4)
(5, 2)
(4, 0)

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 130 lines
#include <bits/stdc++.h>
using namespace std;

struct Point {
    int x, y;
};

int CCW(Point a, Point b, Point c) {

    long long area =
        1LL * (b.x - a.x) * (c.y - a.y)
        - 1LL * (b.y - a.y) * (c.x - a.x);

    if (area < 0)
        return -1;     // clockwise

    if (area > 0)
        return 1;      // counter-clockwise

    return 0;          // collinear
}

// Squared distance
long long distanceSq(Point a, Point b) {

    return 1LL * (a.x - b.x) * (a.x - b.x)
         + 1LL * (a.y - b.y) * (a.y - b.y);
}


void JarvisMarch(vector<Point> points) {

    int n = points.size();

    if (n < 3) {
        cout << "Convex hull cannot be formed.\n";
        return;
    }

    vector<Point> hull;

    int leftmost = 0;

    for (int i = 1; i < n; i++) {

        if (points[i].x < points[leftmost].x ||
            (points[i].x == points[leftmost].x &&
             points[i].y < points[leftmost].y)) {

            leftmost = i;
        }
    }

    // Start from the leftmost point
    int p = leftmost;

    do {

        // Add current point to hull
        hull.push_back(points[p]);

        // Initially select another point
        int q = (p + 1) % n;

        // Find the most counter-clockwise point
        for (int i = 0; i < n; i++) {

            int result = CCW(points[p], points[q], points[i]);

            // Counter-clockwise
            if (result == 1) {
                q = i;
            }

            // Collinear → choose farther point
            else if (result == 0 &&
                     distanceSq(points[p], points[i]) >
                     distanceSq(points[p], points[q])) {

                q = i;
            }
        }

        // Move to next hull point
        p = q;

    } while (p != leftmost);


    // Display result
    cout << "\nConvex Hull Points:\n";

    for (Point p : hull) {
        cout << "(" << p.x << ", " << p.y << ")\n";
    }
}


int main() {

    vector<Point> points = {

        {0, 0},
        {2, 1},
        {4, 0},
        {5, 2},
        {4, 4},
        {2, 5},
        {0, 4},
        {1, 2},

        // Interior points
        {2, 2},
        {3, 2},
        {2, 3},
        {3, 3}
    };


    // Display input points
    cout << "Input Points:\n";

    for (Point p : points) {
        cout << "(" << p.x << ", " << p.y << ")\n";
    }

    JarvisMarch(points);

    return 0;
}

Check your understanding

When can Jarvis beat Graham?

When h is very small: O(nh) can be near linear, versus O(n log n) sorting.

Why is it output-sensitive?

Its running time depends on h, the size of the output hull.

Must two correct hulls print the same sequence?

No. Cyclic rotations and reversed traversal can represent the same polygon; compare boundary vertices and edges.

↑ Back to code index · Choose / compare algorithms

New class code · String

KMP and LPS

COMPLETE CLASSROOM IMPLEMENTATION

Find every occurrence of a nonempty pattern, including overlaps. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: lps[i] = longest proper prefix of pattern[0..i] that is also a suffix; j = matched prefix length.

Read the code in this order

  1. Build LPS: on equality increment len and i; on mismatch with len>0 use len=lps[len−1] without advancing i.
  2. In matching, advance i and j on equality.
  3. At j==m report zero-based index i−j, then use j=lps[j−1] to preserve overlaps.
  4. On mismatch fall back through LPS; advance text i only when j==0.
Time — this implementationAuxiliary space / storage
O(n+m) time, including O(m) preprocessing.O(m) LPS; this exact pass-by-value implementation additionally copies text/pattern, so total auxiliary storage O(n+m).

Assumptions, pitfalls & corrections

  • The supplied main uses text ABABDABACDABABCABAB and pattern ABAB, not stdin. LPS for ABAB is [0,0,1,2]; matches are 0, 10, 15.
  • An empty pattern is unsupported: j−1 can index outside LPS. Decide an empty-pattern policy before adapting.
  • A prefix must be proper (shorter than the whole substring). KMP is substring matching, unlike LCS.
  • The two consecutive if statements are intentional: a match can finish immediately after advancing i and j.
Checked sample input and observed output

No stdin: the sample data are hard-coded in main.

Observed stdout · GNU C++17 build

Pattern found at index 0
Pattern found at index 10
Pattern found at index 15

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 79 lines
#include <iostream>
#include <vector>
#include <string>
using namespace std;

// Build the LPS (Longest Prefix Suffix) array
vector<int> computeLPS(string pattern) {
    int m = pattern.length();
    vector<int> lps(m, 0);

    int len = 0;  // length of previous longest prefix suffix
    int i = 1;

    while (i < m) {
        if (pattern[i] == pattern[len]) {
            len++;
            lps[i] = len;
            i++;
        }
        else {
            if (len != 0) {
                len = lps[len - 1];
            }
            else {
                lps[i] = 0;
                i++;
            }
        }
    }

    return lps;
}

// KMP pattern matching
void KMP(string text, string pattern) {
    int n = text.length();
    int m = pattern.length();

    vector<int> lps = computeLPS(pattern);

    int i = 0;  // index for text
    int j = 0;  // index for pattern

    while (i < n) {

        if (text[i] == pattern[j]) {
            i++;
            j++;
        }

        // Pattern completely matched
        if (j == m) {
            cout << "Pattern found at index " << i - j << endl;

            // Continue searching for overlapping matches
            j = lps[j - 1];
        }

        // Mismatch after some matches
        else if (i < n && text[i] != pattern[j]) {

            if (j != 0) {
                j = lps[j - 1];
            }
            else {
                i++;
            }
        }
    }
}

int main() {
    string text = "ABABDABACDABABCABAB";
    string pattern = "ABAB";

    KMP(text, pattern);

    return 0;
}

Check your understanding

Why not reset j to zero after a match?

The LPS fallback retains a possible overlapping prefix, e.g. AAA occurs at 0,1,2 in AAAAA.

Why is the algorithm linear despite its fallback loop?

The text index never moves backward; increases and decreases of the matched-prefix length are amortized linear.

Why not increment i when len falls back during LPS construction?

The same pattern character must be tested against a shorter candidate prefix.

↑ Back to code index · Choose / compare algorithms

Divide and conquer

Merge Sort

COMPLETE CLASSROOM IMPLEMENTATION

Stable ascending comparison sort. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: Inclusive range [low,high]; sorted halves [low,mid] and [mid+1,high].

Read the code in this order

  1. Stop when low>=high.
  2. Recursively sort both halves.
  3. Merge with two pointers; take the left element on equality.
  4. Append either remainder and copy temp into arr[low+k].
Time — this implementationAuxiliary space / storage
Θ(n log n) best/average/worst.O(n) temporary storage plus O(log n) call stack.

Assumptions, pitfalls & corrections

  • The <= comparison makes equal keys from the left half stay before the right half (stability).
  • Not in-place in the O(1)-auxiliary-space sense.
  • For an empty vector, prefer an explicit empty guard rather than relying on unsigned size()-1 conversion.
Checked sample input and observed output

No stdin: the sample data are hard-coded in main.

Observed stdout · GNU C++17 build

Sorted Array: 3 9 10 27 38 43 82 

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 76 lines
#include <iostream>
#include <vector>

using namespace std;

void merge(vector<int> &arr, int low, int mid, int high)
{
    vector<int> temp;

    int i = low;
    int j = mid + 1;

    // Compare elements from left and right subarrays
    while (i <= mid && j <= high)
    {
        if (arr[i] <= arr[j])
        {
            temp.push_back(arr[i]);
            i++;
        }
        else
        {
            temp.push_back(arr[j]);
            j++;
        }
    }

    // Copy remaining elements from left subarray
    while (i <= mid)
    {
        temp.push_back(arr[i]);
        i++;
    }

    // Copy remaining elements from right subarray
    while (j <= high)
    {
        temp.push_back(arr[j]);
        j++;
    }

    // Copy sorted elements back to original array
    for (int k = 0; k < temp.size(); k++)
    {
        arr[low + k] = temp[k];
    }
}

void mergeSort(vector<int> &arr, int low, int high)
{
    if (low < high)
    {
        int mid = (low + high) / 2;

        mergeSort(arr, low, mid);
        mergeSort(arr, mid + 1, high);

        merge(arr, low, mid, high);
    }
}

int main()
{
    vector<int> arr = {38, 27, 43, 3, 9, 82, 10};

    mergeSort(arr, 0, arr.size() - 1);

    cout << "Sorted Array: ";

    for (int x : arr)
    {
        cout << x << " ";
    }

    return 0;
}

Check your understanding

Recurrence?

T(n)=2T(n/2)+Θ(n), hence Θ(n log n).

Why copy to low+k?

temp starts at zero while the original subarray starts at low.

↑ Back to code index · Choose / compare algorithms

Divide and conquer

Divide-and-Conquer Min–Max

COMPLETE CLASSROOM IMPLEMENTATION

Return minimum and maximum together. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: Pair {minimum,maximum} for inclusive [low,high].

Read the code in this order

  1. For one element return it as both extremes.
  2. For two elements make one comparison.
  3. Solve both halves, then compare the two minima and the two maxima.
Time — this implementationAuxiliary space / storage
Θ(n); for n a power of two (n≥2), 3n/2−2 comparisons.O(log n) recursion; array passed by reference.

Assumptions, pitfalls & corrections

  • The exact 3n/2−2 count is not universal for arbitrary n under this midpoint split.
  • Empty input is unsupported because no low>high base case exists.
Checked sample input and observed output

No stdin: the sample data are hard-coded in main.

Observed stdout · GNU C++17 build

Minimum = 1
Maximum = 3000

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 62 lines
#include <iostream>
#include <vector>

using namespace std;

struct Pair
{
    int minimum;
    int maximum;
};

Pair findMinMax(vector<int> &arr, int low, int high)
{
    Pair result;

    // Only one element
    if (low == high)
    {
        result.minimum = arr[low];
        result.maximum = arr[low];
        return result;
    }

    // Two elements
    if (low + 1 == high)
    {
        if (arr[low] < arr[high])
        {
            result.minimum = arr[low];
            result.maximum = arr[high];
        }
        else
        {
            result.minimum = arr[high];
            result.maximum = arr[low];
        }

        return result;
    }

    int mid = (low + high) / 2;

    Pair left = findMinMax(arr, low, mid);
    Pair right = findMinMax(arr, mid + 1, high);

    result.minimum = min(left.minimum, right.minimum);
    result.maximum = max(left.maximum, right.maximum);

    return result;
}

int main()
{
    vector<int> arr = {100, 11, 445, 1, 330, 3000};

    Pair ans = findMinMax(arr, 0, arr.size() - 1);

    cout << "Minimum = " << ans.minimum << endl;
    cout << "Maximum = " << ans.maximum << endl;

    return 0;
}

Check your understanding

Why special-case two elements?

One comparison determines both min and max, reducing comparison count.

Is it asymptotically faster than a linear scan?

No; both are Θ(n). The benefit is the number of comparisons.

↑ Back to code index · Choose / compare algorithms

Divide and conquer

Quicksort · First Pivot

COMPLETE CLASSROOM IMPLEMENTATION

Partition around the first element and recursively sort the two sides. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: pivot=arr[low]; i scans forward; j scans backward; final pivot index is j.

Read the code in this order

  1. Move i while arr[i]<=pivot and j while arr[j]>pivot.
  2. Swap misplaced elements while i<j.
  3. At crossing swap arr[low] with arr[j].
  4. Recurse on [low,j−1] and [j+1,high], excluding the settled pivot.
Time — this implementationAuxiliary space / storage
Average Θ(n log n); worst Θ(n²), including sorted/reverse-sorted/all-equal adversarial inputs.O(log n) average stack, O(n) worst stack; O(1) partition workspace.

Assumptions, pitfalls & corrections

  • This is a two-pointer pivot-placement partition; do not substitute the recursion bounds of classic Hoare partition, which returns a split rather than a settled pivot.
  • The left-to-right && guard prevents reading arr[i] beyond high.
  • The pivot acts as a sentinel for j. Equal values can produce severely unbalanced splits; this sort is not stable.
Checked sample input and observed output

No stdin: the sample data are hard-coded in main.

Observed stdout · GNU C++17 build

Sorted Array: 10 20 30 40 50 60 70 80 90 

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 63 lines
#include <iostream>
#include <vector>

using namespace std;

int partition(vector<int> &arr, int low, int high)
{
    int pivot = arr[low];

    int i = low + 1;
    int j = high;

    while (true)
    {
        while (i <= high && arr[i] <= pivot)
        {
            i++;
        }

        while (arr[j] > pivot)
        {
            j--;
        }

        if (i < j)
        {
            swap(arr[i], arr[j]);
        }
        else
        {   swap(arr[low], arr[j]);
            break;
        }
    }

    return j;
}

void quickSort(vector<int> &arr, int low, int high)
{
    if (low < high)
    {
        int pivotIndex = partition(arr, low, high);

        quickSort(arr, low, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, high);
    }
}

int main()
{
    vector<int> arr = {50, 70, 60, 90, 40, 80, 10, 20, 30};

    quickSort(arr, 0, arr.size() - 1);

    cout << "Sorted Array: ";

    for (int x : arr)
    {
        cout << x << " ";
    }

    return 0;
}

Check your understanding

Why exclude j from recursion?

The pivot has been moved to its final sorted position.

Why does sorted input hurt?

The first pivot is an extreme, leaving subproblems of sizes 0 and n−1.

↑ Back to code index · Choose / compare algorithms

Divide and conquer

Quicksort · Pivot Choices

COMPLETE CLASSROOM IMPLEMENTATION

Compare first, middle-index and random pivot choices with the same partition. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: pivotChoice 1=first, 2=middle index, 3=random index; chosen pivot is swapped into low.

Read the code in this order

  1. Read the choice; seed rand with time(0).
  2. Move the chosen pivot to the first position.
  3. Apply the same two-pointer partition and recursive bounds as the first-pivot listing.
Time — this implementationAuxiliary space / storage
Worst Θ(n²) for every choice; randomized expected Θ(n log n) under suitable distinct-key inputs.Average O(log n), worst O(n) stack.

Assumptions, pitfalls & corrections

  • Middle INDEX is not median VALUE. It gives no worst-case guarantee.
  • All-equal input remains quadratic even with a random pivot in this partition.
  • rand()%range is not perfectly uniform; fixed seeds are better for reproducible experiments. Invalid choices silently behave like first pivot.
Checked sample input and observed output

Standard input

2

Observed stdout · GNU C++17 build

Choose Pivot Method:
1. First Element
2. Middle Element
3. Random Element
Enter Choice: 
Sorted Array:
10 20 30 40 50 60 70 80 90 

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 83 lines
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>

using namespace std;

// 1 = First Element
// 2 = Middle Element
// 3 = Random Element

int partition(vector<int> &arr, int low, int high, int pivotChoice)
{
    // Choose Pivot
    if (pivotChoice == 2)
    {
        int mid = (low + high) / 2;
        swap(arr[low], arr[mid]);
    }
    else if (pivotChoice == 3)
    {
        int randomIndex = low + rand() % (high - low + 1);
        swap(arr[low], arr[randomIndex]);
    }

    // Hoare Partition
    int pivot = arr[low];

    int i = low + 1;
    int j = high;

    while (true)
    {
        while (i <= high && arr[i] <= pivot)
            i++;

        while (arr[j] > pivot)
            j--;

        if (i < j)
            swap(arr[i], arr[j]);
        else
            break;
    }

    swap(arr[low], arr[j]);

    return j;
}

void quickSort(vector<int> &arr, int low, int high,int pivotChoice)
{
    if (low < high)
    {
        int pivotIndex = partition(arr, low, high, pivotChoice);

        quickSort(arr, low, pivotIndex - 1,pivotChoice);
        quickSort(arr, pivotIndex + 1, high,pivotChoice);
    }
}

int main()
{
    srand(time(0));
    int pivotChoice;
    vector<int> arr = {50, 70, 60, 90, 40, 80, 10, 20, 30};

    cout << "Choose Pivot Method:\n";
    cout << "1. First Element\n";
    cout << "2. Middle Element\n";
    cout << "3. Random Element\n";
    cout << "Enter Choice: ";
    cin >> pivotChoice;

    quickSort(arr, 0, arr.size() - 1,pivotChoice);

    cout << "\nSorted Array:\n";

    for (int x : arr)
        cout << x << " ";

    return 0;
}

Check your understanding

Does randomness eliminate worst-case time?

No. It reduces the chance of consistently bad splits; the worst case still exists.

Randomized quicksort: Monte Carlo or Las Vegas?

Las Vegas: sorting correctness is retained, while runtime depends on random choices.

↑ Back to code index · Choose / compare algorithms

Greedy

Fractional Knapsack

COMPLETE CLASSROOM IMPLEMENTATION

Maximize profit when items are divisible. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: Items sorted by descending profit/weight; capacity is the remaining weight.

Read the code in this order

  1. Read n, then profit weight pairs, then capacity.
  2. Take a full item if it fits.
  3. Otherwise take fraction=capacity/weight, add fraction*profit, and stop.
Time — this implementationAuxiliary space / storage
O(n log n) sorting + O(n) selection.O(n) items; O(log n) typical std::sort stack.

Assumptions, pitfalls & corrections

  • Input order is PROFIT then WEIGHT, opposite to the 0–1 knapsack listing.
  • Require positive weights, nonnegative capacity and ordinarily nonnegative profits.
  • Fractional greedy does not solve 0–1 knapsack. The ZIP variant below computes correct profit but prints the wrong percentage.
Checked sample input and observed output

Standard input

3
60 10
100 20
120 30
50

Observed stdout · GNU C++17 build

Enter number of items: Enter Profit and Weight for each item:
Enter knapsack capacity: 
Selected Items:
Item 1 -> 100%
Item 2 -> 100%
Item 3 -> 66.6667%

Maximum Profit = 240.00

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 71 lines
#include <iostream>
#include <vector>
#include <algorithm>
#include <iomanip>

using namespace std;

struct Item {
    int id;
    double profit;
    double weight;
};

// Comparator function
bool compare(Item a, Item b) {
    return (a.profit / a.weight) > (b.profit / b.weight);
}

int main() {
    int n;
    cout << "Enter number of items: ";
    cin >> n;

    vector<Item> items(n);

    cout << "Enter Profit and Weight for each item:\n";
    for (int i = 0; i < n; i++) {
        items[i].id = i + 1;
        cin >> items[i].profit >> items[i].weight;
    }

    double capacity;
    cout << "Enter knapsack capacity: ";
    cin >> capacity;

    // Sort according to profit/weight ratio
    sort(items.begin(), items.end(), compare);

    double totalProfit = 0.0;

    cout << "\nSelected Items:\n";

    for (int i = 0; i < n; i++) {
        if (capacity == 0)
            break;

        if (items[i].weight <= capacity) {
            // Take the whole item
            cout << "Item " << items[i].id
                 << " -> 100%\n";

            totalProfit += items[i].profit;
            capacity -= items[i].weight;
        }
        else {
            // Take fraction of the item
            double fraction = capacity / items[i].weight;

            cout << "Item " << items[i].id
                 << " -> " << fraction * 100 << "%\n";

            totalProfit += fraction * items[i].profit;
            capacity = 0;
        }
    }

    cout << fixed << setprecision(2);
    cout << "\nMaximum Profit = " << totalProfit << endl;

    return 0;
}

Check your understanding

Why is ratio greedy correct here?

An exchange of weight from a lower-density item to a higher-density item cannot decrease profit; fractions make the exchange feasible.

At most how many partially selected items?

At most one after ratio sorting.

↑ Back to code index · Choose / compare algorithms

Greedy

Job Sequencing with Deadlines

COMPLETE CLASSROOM IMPLEMENTATION

Maximize profit of unit-time jobs with deadlines on one machine. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: slot[1..D], occupied[1..D], D=max deadline.

Read the code in this order

  1. Sort by descending profit.
  2. For each job search backward from its deadline.
  3. Place it in the latest free positive slot and accumulate profit.
Time — this implementationAuxiliary space / storage
O(n log n+nD+D) for this backward-slot-scan implementation.O(n+D). D can be capped at n in an optimized version.

Assumptions, pitfalls & corrections

  • All jobs must have equal unit duration; do not apply directly to arbitrary durations.
  • Assumes profits are nonnegative; a negative-profit job should be skipped in a generalized maximization task.
  • Using the latest slot preserves earlier slots for tighter deadlines. Deadlines ≤0 yield no placement.
Checked sample input and observed output

Standard input

5
a 2 100
b 1 19
c 2 27
d 1 25
e 3 15

Observed stdout · GNU C++17 build

Enter number of jobs: Enter Job ID, Deadline, Profit:

Selected Jobs:
Time Slot 1 -> Job c
Time Slot 2 -> Job a
Time Slot 3 -> Job e

Maximum Profit = 142

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 66 lines
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

struct Job {
    char id;
    int deadline;
    int profit;
};

// Comparator function
bool compare(Job a, Job b) {
    return a.profit > b.profit;
}

int main() {
    int n;
    cout << "Enter number of jobs: ";
    cin >> n;

    vector<Job> jobs(n);

    cout << "Enter Job ID, Deadline, Profit:\n";
    for (int i = 0; i < n; i++) {
        cin >> jobs[i].id >> jobs[i].deadline >> jobs[i].profit;
    }

    // Sort jobs by profit
    sort(jobs.begin(), jobs.end(), compare);

    // Find maximum deadline
    int maxDeadline = 0;
    for (int i = 0; i < n; i++) {
        maxDeadline = max(maxDeadline, jobs[i].deadline);
    }

    // Slot array
    vector<char> slot(maxDeadline + 1, '-');
    vector<bool> occupied(maxDeadline + 1, false);

    int totalProfit = 0;

    // Schedule jobs
    for (int i = 0; i < n; i++) {
        for (int j = jobs[i].deadline; j >= 1; j--) {
            if (!occupied[j]) {
                occupied[j] = true;
                slot[j] = jobs[i].id;
                totalProfit += jobs[i].profit;
                break;
            }
        }
    }

    cout << "\nSelected Jobs:\n";
    for (int i = 1; i <= maxDeadline; i++) {
        if (occupied[i])
            cout << "Time Slot " << i << " -> Job " << slot[i] << endl;
    }

    cout << "\nMaximum Profit = " << totalProfit << endl;

    return 0;
}

Check your understanding

Why not use the earliest available slot?

A loose-deadline job could block a future tight-deadline job unnecessarily.

What is the structural bottleneck?

Scanning up to D slots per job. A disjoint-set predecessor structure can speed slot selection.

↑ Back to code index · Choose / compare algorithms

Greedy · Graphs

Dijkstra · Adjacency List + Heap

COMPLETE CLASSROOM IMPLEMENTATION

Single-source shortest distances for nonnegative edge weights. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: dist[v]=best source-to-v distance; parent[v]=predecessor; visited finalizes a vertex.

Read the code in this order

  1. Read V E, E lines u v weight, and source; vertices are zero-based.
  2. This main inserts both directions, making the graph undirected.
  3. Extract the minimum tentative distance; ignore already visited entries.
  4. Relax if dist[u]+weight<dist[v], update parent and push the improved pair.
Time — this implementationAuxiliary space / storage
O(V+E log(E+1)) with lazy heap entries; commonly O((V+E) log V) for simple graphs.O(V+E), including adjacency list and up to O(E) heap entries.

Assumptions, pitfalls & corrections

  • Negative edges invalidate the greedy finalization proof. Do not reuse this code for them.
  • INF=1e9 is printed for unreachable vertices; paths ≥INF cannot be represented correctly. int addition may overflow.
  • Requires V>0 and valid source/endpoints. Remove the reverse insertion for directed input.
Checked sample input and observed output

Standard input

5 6
0 1 2
0 2 5
1 2 1
1 3 4
2 3 1
3 4 3
0

Observed stdout · GNU C++17 build

Shortest distances:
0 : 0
1 : 2
2 : 3
3 : 4
4 : 7

Parents:
0 <- -1
1 <- 0
2 <- 1
3 <- 2
4 <- 3

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 72 lines
#include <iostream>
#include <vector>
#include <queue>

using namespace std;

const int INF = 1e9;

int main() {
    int V, E;
    cin >> V >> E;

    vector<vector<pair<int, int>>> adj(V);

    for (int i = 0; i < E; i++) {
        int u, v, w;
        cin >> u >> v >> w;

        adj[u].push_back({v, w});
        adj[v].push_back({u, w});   // remove for directed graph
    }

    int source;
    cin >> source;

    vector<int> dist(V, INF);
    vector<bool> visited(V, false);
    vector<int> parent(V, -1);

    priority_queue<pair<int, int>,
                   vector<pair<int, int>>,
                   greater<pair<int, int>>> pq;

    dist[source] = 0;
    parent[source] = -1;
    pq.push({0, source});

    while (!pq.empty()) {

        int d = pq.top().first;
        int u = pq.top().second;
        pq.pop();

        if (visited[u])
            continue;



        for (auto edge : adj[u]) {
            int v = edge.first;
            int weight = edge.second;

            if (!visited[v] && dist[u] + weight < dist[v]) {
                dist[v] = dist[u] + weight;
                parent[v] = u;
                pq.push({dist[v], v});
            }
        }

        visited[u] = true;
    }

    cout << "Shortest distances:\n";
    for (int i = 0; i < V; i++)
        cout << i << " : " << dist[i] << endl;

    cout << "\nParents:\n";
    for (int i = 0; i < V; i++)
        cout << i << " <- " << parent[i] << endl;

    return 0;
}

Check your understanding

Dijkstra relaxation versus Prim relaxation?

Dijkstra compares dist[u]+w for a whole path; Prim compares w for a single crossing edge.

Why may a heap contain duplicates?

Each improvement pushes a new pair; visited skips obsolete entries when popped.

↑ Back to code index · Choose / compare algorithms

Greedy · Graphs

Prim · Minimum Spanning Tree

COMPLETE CLASSROOM IMPLEMENTATION

Connect all vertices of a connected undirected weighted graph with minimum total edge weight. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: key[v]=lightest known edge joining v to the growing tree; parent[v]=that edge’s tree endpoint.

Read the code in this order

  1. Use the same V E / edges / source input format as Dijkstra.
  2. Extract the vertex with smallest key.
  3. For unvisited v, improve key[v] using edge weight, NOT a path distance.
  4. Print parent edges and sum key values.
Time — this implementationAuxiliary space / storage
O(V+E log(E+1)) for this lazy heap implementation; commonly O((V+E) log V) for simple graphs.O(V+E).

Assumptions, pitfalls & corrections

  • On a disconnected graph this program returns only the source component, not a full spanning forest.
  • Negative weights are allowed for MST, unlike Dijkstra.
  • Self-loops should be rejected: visited[u] is set after neighbor scanning, so a negative self-loop can corrupt parent/key. INF and int totals also need safe bounds.
Checked sample input and observed output

Standard input

5 6
0 1 2
0 2 5
1 2 1
1 3 4
2 3 1
3 4 3
0

Observed stdout · GNU C++17 build

MST Edges:
0 - 1 (weight = 2)
1 - 2 (weight = 1)
2 - 3 (weight = 1)
3 - 4 (weight = 3)

Total MST Cost = 7

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 77 lines
#include <iostream>
#include <vector>
#include <queue>

using namespace std;

const int INF = 1e9;

int main() {
    int V, E;
    cin >> V >> E;

    vector<vector<pair<int, int>>> adj(V);

    for (int i = 0; i < E; i++) {
        int u, v, w;
        cin >> u >> v >> w;

        adj[u].push_back({v, w});
        adj[v].push_back({u, w});   // undirected graph
    }

    int source;
    cin >> source;

    vector<int> key(V, INF);      // minimum edge weight to reach node
    vector<bool> visited(V, false);
    vector<int> parent(V, -1);

    // (weight, node)
    priority_queue<pair<int, int>,
                   vector<pair<int, int>>,
                   greater<pair<int, int>>> pq;

    key[source] = 0;
    pq.push({0, source});

    while (!pq.empty()) {

        int w = pq.top().first;
        int u = pq.top().second;
        pq.pop();

        if (visited[u])
            continue;



        for (auto edge : adj[u]) {
            int v = edge.first;
            int weight = edge.second;

            if (!visited[v] && weight < key[v]) {
                key[v] = weight;
                pq.push({key[v], v});
                parent[v] = u;
            }
        }
        visited[u] = true;
    }

    // Output MST edges
    cout << "\nMST Edges:\n";
    int totalCost = 0;

    for (int i = 0; i < V; i++) {
        if (parent[i] != -1) {
            cout << parent[i] << " - " << i
                 << " (weight = " << key[i] << ")\n";
            totalCost += key[i];
        }
    }

    cout << "\nTotal MST Cost = " << totalCost << endl;

    return 0;
}

Check your understanding

Why is the chosen crossing edge safe?

By the cut property, a lightest edge crossing the current tree/rest cut belongs to some MST.

Does an MST minimize each source-to-vertex path?

No. Minimum total tree weight and shortest individual paths are different objectives.

↑ Back to code index · Choose / compare algorithms

Dynamic programming

0–1 Knapsack · DP + Reconstruction

COMPLETE CLASSROOM IMPLEMENTATION

Maximize profit with each item used at most once. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: dp[i][w]=best profit using the first i items and capacity w.

Read the code in this order

  1. Read n W, then WEIGHT PROFIT for each item.
  2. Skip: dp[i−1][w]. Take: profit[i]+dp[i−1][w−weight[i]]. Keep the maximum.
  3. Backtrack from (n,W); select i when dp[i][w]!=dp[i−1][w].
  4. Subtract selected weight and continue to the previous item; reverse the selected index list.
Time — this implementationAuxiliary space / storage
O(nW), pseudo-polynomial in numeric capacity W.O(nW) table + O(n) inputs/selection.

Assumptions, pitfalls & corrections

  • Taking uses the PREVIOUS row: changing i−1 to i permits reuse and changes the problem.
  • A tied value can skip an item; multiple optimal subsets may exist.
  • Nonnegative integer weights/capacity are required; oversized W can exhaust memory. A 1D value-only DP needs descending capacities to prevent reuse.
Checked sample input and observed output

Standard input

3 50
10 60
20 100
30 120

Observed stdout · GNU C++17 build

Enter number of objects and knapsack capacity: 
Enter weight and profit of object 1: 
Enter weight and profit of object 2: 
Enter weight and profit of object 3: 
0 0 0 0 0 0 0 0 0 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 
0 0 0 0 0 0 0 0 0 60 60 60 60 60 60 60 60 60 60 100 100 100 100 100 100 100 100 100 100 160 160 160 160 160 160 160 160 160 160 160 160 160 160 160 160 160 160 160 160 160 
0 0 0 0 0 0 0 0 0 60 60 60 60 60 60 60 60 60 60 100 100 100 100 100 100 100 100 100 100 160 160 160 160 160 160 160 160 160 160 180 180 180 180 180 180 180 180 180 180 220 
Maximum Profit = 220
Selected items:
Object	Weight	Profit
2	20	100
3	30	120

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 61 lines
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    int n, W;
    cout << "Enter number of objects and knapsack capacity: " << endl;
    cin >> n >> W;

    vector<int> weight(n+1), profit(n+1);

    for (int i = 1; i <= n; i++) {
        cout << "Enter weight and profit of object " << i << ": " << endl;
        cin >> weight[i] >> profit[i];
    }

    vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));

    // Build DP table
    for (int i = 1; i <= n; i++) {
        for (int w = 0; w <= W; w++) {
            if (weight[i] <= w) {
                dp[i][w] = max(dp[i - 1][w],
                               profit[i] + dp[i - 1][w - weight[i]]);
            } else {
                dp[i][w] = dp[i - 1][w];
            }
        }
    }

     for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= W; j++) {
          cout<<dp[i][j]<<" ";
        }
        cout<<endl;
     }

    cout << "Maximum Profit = " << dp[n][W] << endl;

    // Find selected items
    vector<int> selected;
    int w = W;

    for (int i = n; i > 0; i--) {
        if (dp[i][w] != dp[i - 1][w]) {
            selected.push_back(i);
            w -= weight[i];
        }
    }

    reverse(selected.begin(), selected.end());

    cout << "Selected items:\n";
    cout << "Object\tWeight\tProfit\n";
    for (int item : selected)
        cout << item << "\t" << weight[item] << "\t" << profit[item] << endl;

    return 0;
}

Check your understanding

Why not ratio greedy?

Indivisibility prevents the exchange argument; a locally best density can block a better combination.

Why pseudo-polynomial?

The table depends on W, while W needs only O(log W) bits in the input.

↑ Back to code index · Choose / compare algorithms

Dynamic programming

Coin Change · Minimum Coins

COMPLETE CLASSROOM IMPLEMENTATION

Find a minimum-count representation using unlimited positive-denomination coins. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: dp[i][j]=minimum coins to make j using coin types 1..i; INF means impossible.

Read the code in this order

  1. Initialize every dp[i][0]=0 and other states INF.
  2. Skip type i via dp[i−1][j], or reuse it via 1+dp[i][j−coin[i]].
  3. During reconstruction, move to i−1 if the previous row ties; otherwise take coin[i] and stay on row i.
Time — this implementationAuxiliary space / storage
O(nA), A=amount; reconstruction O(n+A/cmin) for positive minimum coin cmin.O(nA) table, plus selected coins O(A/cmin).

Assumptions, pitfalls & corrections

  • This is MINIMUM COUNT, not number of ways. The current row permits unlimited reuse.
  • All coin values must be positive and amount nonnegative. Zero/negative coins break the recurrence or reconstruction.
  • INF=INT_MAX/2 avoids overflow when adding one. Coin 4 with coins {1,3,4} cannot be greedily repeated to optimally make 6: 3+3 is better than 4+1+1.
Checked sample input and observed output

Standard input

3 6
1
3
4

Observed stdout · GNU C++17 build

Enter number of coin types and target amount
Enter value of coin 1
Enter value of coin 2
Enter value of coin 3
1 2 3 4 5 6 
1 2 1 2 3 2 
1 2 1 1 2 2 
Minimum number of coins = 2
Coins selected: 3 3 

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 78 lines
#include <algorithm>
#include <climits>
#include <iostream>
#include <vector>


using namespace std;

int main() {
  int n, amount;

  cout << "Enter number of coin types and target amount" << endl;
  cin >> n >> amount;

  vector<int> coin(n + 1);

  for (int i = 1; i <= n; i++) {
    cout << "Enter value of coin " << i << endl;
    cin >> coin[i];
  }

  const int INF = INT_MAX / 2;

  vector<vector<int>> dp(n + 1, vector<int>(amount + 1, INF));

  // Base cases
  for (int i = 0; i <= n; i++)
    dp[i][0] = 0;

  // Build DP table
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= amount; j++) {

      if (coin[i] <= j) {
        dp[i][j] = min(dp[i - 1][j], dp[i][j - coin[i]] + 1);
      } else {
        dp[i][j] = dp[i - 1][j];
      }
    }
  }

  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= amount; j++) {
      cout << dp[i][j] << " ";
    }
    cout << endl;
  }

  if (dp[n][amount] == INF) {
    cout << "No solution possible." << endl;
    return 0;
  }

  cout << "Minimum number of coins = " << dp[n][amount] << endl;

  // Find selected coins
  vector<int> selected;
  int j = amount;

  for (int i = n; i > 0 && j > 0;) {

    if (dp[i][j] == dp[i - 1][j]) {
      i--;
    } else {
      selected.push_back(coin[i]);
      j -= coin[i];
    }
  }

  reverse(selected.begin(), selected.end());

  cout << "Coins selected: ";
  for (int c : selected)
    cout << c << " ";
  cout << endl;

  return 0;
}

Check your understanding

Why stay on row i after taking a coin?

The same denomination remains available without a usage limit.

Base cases?

Zero coins form amount zero with count zero; positive amounts with no types are impossible.

↑ Back to code index · Choose / compare algorithms

Dynamic programming

Longest Common Subsequence

COMPLETE CLASSROOM IMPLEMENTATION

Find a longest order-preserving subsequence shared by two strings. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: dp[i][j]=LCS length of prefixes s1[0..i−1], s2[0..j−1].

Read the code in this order

  1. On equal characters use diagonal+1.
  2. Otherwise take max(top,left).
  3. Backtrack: equal characters go diagonal and append; otherwise follow a larger neighbor.
  4. On a tie the supplied code goes left; reverse the collected string.
Time — this implementationAuxiliary space / storage
O(nm); backtracking O(n+m).O(nm) table + O(min(n,m)) reconstructed answer.

Assumptions, pitfalls & corrections

  • Subsequence need not be contiguous; substring must be contiguous.
  • cin>>string cannot read spaces or an empty line as a string.
  • The extra commented dp check is redundant for a correctly constructed LCS table when characters match. Several valid LCS strings can tie.
Checked sample input and observed output

Standard input

ABCBDAB
BDCABA

Observed stdout · GNU C++17 build

Enter first string
Enter second string
0 0 0 1 1 1 
1 1 1 1 2 2 
1 1 2 2 2 2 
1 1 2 2 3 3 
1 2 2 2 3 3 
1 2 2 3 3 4 
1 2 2 3 4 4 
Length of LCS = 4
Longest Common Subsequence = BDAB

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 72 lines
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

int main() {
    string s1, s2;

    cout << "Enter first string" << endl;
    cin >> s1;

    cout << "Enter second string" << endl;
    cin >> s2;

    int n = s1.length();
    int m = s2.length();

    vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));

    // Build DP table
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {

            if (s1[i - 1] == s2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            } else {
                dp[i][j] = max(dp[i - 1][j],
                               dp[i][j - 1]);
            }

        }
    }

     for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
          cout<<dp[i][j]<<" ";
        }
        cout<<endl;
     }

    cout << "Length of LCS = " << dp[n][m] << endl;

    // Find the LCS
    string lcs = "";

    int i = n;
    int j = m;

    while (i > 0 && j > 0) {

        if (s1[i - 1] == s2[j - 1]) { /// for safety, add, if (  && dp[i][j] == dp[i-1][j-1] + 1)
            lcs += s1[i - 1];
            i--;
            j--;
        }
        else if (dp[i - 1][j] > dp[i][j - 1]) {
            i--;
        }
        else {
            j--;
        }

    }

    reverse(lcs.begin(), lcs.end());

    cout << "Longest Common Subsequence = " << lcs << endl;

    return 0;
}

Check your understanding

Why is a zero row and column needed?

An empty prefix has LCS length zero and provides boundary states.

Can memory be reduced?

Two rows give the length in O(min(n,m)) space, but ordinary reconstruction requires more information.

↑ Back to code index · Choose / compare algorithms

Dynamic programming

Subset Sum · Boolean DP

COMPLETE CLASSROOM IMPLEMENTATION

Decide whether a subset reaches a target and reconstruct one solution. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: dp[i][j]=whether sum j is possible from the first i elements.

Read the code in this order

  1. Set dp[i][0]=true.
  2. Skip with dp[i−1][j] OR take with dp[i−1][j−arr[i]].
  3. If the final state is true, backtrack; include an element when the value differs from the previous row.
Time — this implementationAuxiliary space / storage
O(nT), T=target; pseudo-polynomial.O(nT) Boolean entries (vector<bool> bit-packs its row storage).

Assumptions, pitfalls & corrections

  • Requires nonnegative target/elements; negative elements need a different state representation.
  • This SOS means SUM OF SUBSETS, not bitmask sum-over-subsets DP.
  • Prints one feasible subset, not every feasible subset. Target zero returns the empty subset.
Checked sample input and observed output

Standard input

6 9
3 34 4 12 5 2

Observed stdout · GNU C++17 build

Enter number of elements and target sum
Enter value of element 1
Enter value of element 2
Enter value of element 3
Enter value of element 4
Enter value of element 5
Enter value of element 6
0 0 1 0 0 0 0 0 0 
0 0 1 0 0 0 0 0 0 
0 0 1 1 0 0 1 0 0 
0 0 1 1 0 0 1 0 0 
0 0 1 1 1 0 1 1 1 
0 1 1 1 1 1 1 1 1 
Subset exists.
Selected elements: 4 5 

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 76 lines
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {

    int n, target;

    cout << "Enter number of elements and target sum" << endl;
    cin >> n >> target;

    vector<int> arr(n + 1);

    for (int i = 1; i <= n; i++) {
        cout << "Enter value of element " << i << endl;
        cin >> arr[i];
    }

    vector<vector<bool>> dp(n + 1, vector<bool>(target + 1, false));

    // Base cases
    for (int i = 0; i <= n; i++)
        dp[i][0] = true;

    // Build DP table
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= target; j++) {

            if (arr[i] <= j) {
                dp[i][j] = dp[i - 1][j] || dp[i - 1][j - arr[i]];
            } else {
                dp[i][j] = dp[i - 1][j];
            }

        }
    }

     for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= target; j++) {
          cout<<dp[i][j]<<" ";
        }
        cout<<endl;
     }

    if (!dp[n][target]) {
        cout << "No subset exists." << endl;
        return 0;
    }

    cout << "Subset exists." << endl;

    // Find selected elements
    vector<int> selected;
    int j = target;

    for (int i = n; i > 0 && j > 0; i--) {

        if (dp[i][j] != dp[i - 1][j]) {
            selected.push_back(arr[i]);
            j -= arr[i];
        }

    }

    reverse(selected.begin(), selected.end());

    cout << "Selected elements: ";
    for (int x : selected)
        cout << x << " ";

    cout << endl;

    return 0;
}

Check your understanding

Why the previous row when taking?

Each array position may be used at most once.

DP versus backtracking subset sum?

DP merges equal (prefix,sum) states; backtracking traverses individual include/exclude choices.

↑ Back to code index · Choose / compare algorithms

Backtracking

Sum of Subsets · Backtracking

COMPLETE CLASSROOM IMPLEMENTATION

Enumerate target-sum subsets with include/exclude recursion and bounds. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: i = next index; currentSum = chosen sum; remainingSum = sum of undecided weights; soln marks selection.

Read the code in this order

  1. If currentSum==target, print the chosen weights and return.
  2. Stop past n, when already over target, or when even all remaining values cannot reach target.
  3. Set soln[i]=1 and recurse with inclusion.
  4. Reset soln[i]=0 and recurse with exclusion.
Time — this implementationAuxiliary space / storage
O(2ⁿ) search nodes; O(n·2ⁿ) conservative bound including printing.O(n) recursion and selection.

Assumptions, pitfalls & corrections

  • The pruning assumes nonnegative weights; negative weights make it unsound.
  • Strictly positive weights are needed for complete enumeration with the early-success return: zero extensions are otherwise omitted.
  • Duplicate values can produce identical printed subsets from different indices; they are not deduplicated.
Checked sample input and observed output

Standard input

4
2 3 5 7
10

Observed stdout · GNU C++17 build

Enter no. of items stored in the array
Enter 1 th item, stored in the array
Enter 2 th item, stored in the array
Enter 3 th item, stored in the array
Enter 4 th item, stored in the array
Enter Target Sum
Possible subsets:
{ 2 3 5 }
{ 3 7 }

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 70 lines
#include <iostream>
#include <vector>
using namespace std;

int n, target;
vector<int> w;         // 1-indexed
vector<int> soln;      // soln[i] = 1 if w[i] is selected

void sumOfSubsets(int i, int currentSum, int remainingSum)
{
    if (currentSum == target)
    {
        cout << "{ ";
        for (int j = 1; j <= n; j++)
        {
            if (soln[j])
                cout << w[j] << " ";
        }
        cout << "}" << endl;
        return;
    }

    if (i > n)
        return;

    if (currentSum > target)
        return;

    // Bounding condition
    if (currentSum + remainingSum < target)
        return;

    // Include current element
    soln[i] = 1;
    sumOfSubsets(i + 1,
                 currentSum + w[i],
                 remainingSum - w[i]);

    // Exclude current element
    soln[i] = 0;
    sumOfSubsets(i + 1,
                 currentSum,
                 remainingSum - w[i]);
}

int main()
{
    cout<<"Enter no. of items stored in the array"<<endl;
    cin >> n;

    w.resize(n + 1);
    soln.resize(n + 1, 0);

    int remainingSum = 0;

    for (int i = 1; i <= n; i++)
    {
        cout<<"Enter "<<(i)<<" th item, stored in the array"<<endl;
        cin >> w[i];
        remainingSum += w[i];
    }

    cout<<"Enter Target Sum"<<endl;
    cin >> target;

    cout << "Possible subsets:\n";
    sumOfSubsets(1, 0, remainingSum);

    return 0;
}

Check your understanding

What does remainingSum bound?

The largest additional sum possible if every undecided nonnegative item is taken.

Why undo soln[i]?

The exclusion branch must not inherit the inclusion choice.

↑ Back to code index · Choose / compare algorithms

Backtracking

N-Queens · All Solutions

COMPLETE CLASSROOM IMPLEMENTATION

Place n queens on an n×n board so none attack each other. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: soln[row]=chosen column; rows and columns are one-based.

Read the code in this order

  1. Try every column in the current row.
  2. Against each earlier row, reject same column or equal absolute row/column difference.
  3. Place, recurse to the next row, then reset.
  4. At row>n print the column vector.
Time — this implementationAuxiliary space / storage
Exponential; O(n²·n!) is a safe bound for this full-column-loop + O(n) safety-check version, plus solution output.O(n) chosen columns and recursion.

Assumptions, pitfalls & corrections

  • Do not describe this exact implementation as O(1) safety checking; it scans earlier rows.
  • n=1 has one solution; n=2,3 have none; n=4 prints two.
  • For faster checks track used columns and both diagonals; all solutions still require output time.
Checked sample input and observed output

Standard input

4

Observed stdout · GNU C++17 build

Enter value of n: Solution 1:
2 4 1 3 

Solution 2:
3 1 4 2 

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 58 lines
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

int n;
vector<int> soln;   // soln[row] = column where the queen is placed

bool isSafe(int row, int col)
{
    for (int i = 1; i < row; i++)
    {
        // Same column or same diagonal
        if (soln[i] == col || abs(soln[i] - col) == abs(i - row))
            return false;
    }
    return true;
}

void nQueens(int row)
{
    if (row > n)
    {
        static int solutionNo = 1;

        cout << "Solution " << solutionNo++ << ":\n";

        for (int i = 1; i <= n; i++)
            cout << soln[i] << " ";
        cout << "\n\n";

        return;
    }

    for (int col = 1; col <= n; col++)
    {
        if (isSafe(row, col))
        {
            soln[row] = col;

            nQueens(row + 1);

            soln[row] = 0;   // Backtrack
        }
    }
}

int main()
{
    cout << "Enter value of n: ";
    cin >> n;

    soln.resize(n + 1, 0);

    nQueens(1);

    return 0;
}

Check your understanding

Why no same-row check?

The recursion places exactly one queen per row.

Diagonal condition?

|column difference| equals |row difference|.

↑ Back to code index · Choose / compare algorithms

Backtracking

M-Coloring · All Assignments

COMPLETE CLASSROOM IMPLEMENTATION

Enumerate proper vertex colorings using at most m labeled colors. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: soln[v]=color 1..m; 0 means unassigned; graph is a one-based adjacency matrix.

Read the code in this order

  1. Read n, m, then n×n adjacency entries.
  2. For each vertex try every color.
  3. Reject if an adjacent vertex already has that color.
  4. Assign, recurse, reset; at vertex>n print the coloring.
Time — this implementationAuxiliary space / storage
O(n·mⁿ) worst case for m≥2 with linear safety scans; O(n²) scanning when m=1 reaches every level.O(n²) graph + O(n) state/stack.

Assumptions, pitfalls & corrections

  • Requires a simple undirected graph with a symmetric 0/1 matrix and zero diagonal.
  • A self-loop is not correctly rejected: the current vertex is unassigned during isSafe.
  • This tests a supplied m; it does not directly compute the chromatic number. Swapping color labels counts as a different output.
Checked sample input and observed output

Standard input

3
3
0 1 1
1 0 1
1 1 0

Observed stdout · GNU C++17 build

Enter number of vertices: Enter number of colors: Enter adjacency matrix:
Solution 1:
1 2 3 

Solution 2:
1 3 2 

Solution 3:
2 1 3 

Solution 4:
2 3 1 

Solution 5:
3 1 2 

Solution 6:
3 2 1 

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 69 lines
#include <iostream>
#include <vector>
using namespace std;

int n, m;
vector<vector<int>> graph;
vector<int> soln;

bool isSafe(int vertex, int color)
{
    for (int i = 1; i <= n; i++)
    {
        if (graph[vertex][i] == 1 && soln[i] == color)
            return false;
    }

    return true;
}

void mColoring(int vertex)
{
    if (vertex > n)
    {
        static int solutionNo = 1;

        cout << "Solution " << solutionNo++ << ":\n";

        for (int i = 1; i <= n; i++)
            cout << soln[i] << " ";

        cout << "\n\n";
        return;
    }

    for (int color = 1; color <= m; color++)
    {
        if (isSafe(vertex, color))
        {
            soln[vertex] = color;

            mColoring(vertex + 1);

            soln[vertex] = 0;   // Backtrack
        }
    }
}

int main()
{
    cout << "Enter number of vertices: ";
    cin >> n;

    cout << "Enter number of colors: ";
    cin >> m;

    graph.resize(n + 1, vector<int>(n + 1));
    soln.resize(n + 1, 0);

    cout << "Enter adjacency matrix:\n";
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
            cin >> graph[i][j];
    }

    mColoring(1);

    return 0;
}

Check your understanding

Decision versus enumeration?

Decision stops at the first coloring; this code continues to enumerate all labeled assignments.

What is the chromatic number?

The minimum m for which a valid proper coloring exists.

↑ Back to code index · Choose / compare algorithms

Network flow

Edmonds–Karp · Residual Matrix

COMPLETE CLASSROOM IMPLEMENTATION

Find maximum source-to-sink flow with BFS augmenting paths. Theory explanation ↗

Original C++ is preserved; the explanation and caveats below are separate study annotations.

State to explain: rGraph = residual capacity matrix; parent reconstructs a BFS path.

Read the code in this order

  1. Copy capacity graph into residual graph.
  2. BFS uses only positive residual edges and minimizes number of edges, not edge weights.
  3. Find path_flow as the minimum residual capacity on the parent chain.
  4. Subtract from forward residual edges, add to reverse edges, and add to max_flow.
  5. Stop when no augmenting path remains.
Time — this implementationAuxiliary space / storage
Standard adjacency-list Edmonds–Karp: O(VE²). This matrix-scanning version: O(V³E) via O(VE) augmentations × O(V²) BFS.O(V²) residual matrix + O(V) BFS arrays/queue; supplied main fixes V=6.

Assumptions, pitfalls & corrections

  • Reverse residual capacity allows earlier routing choices to be canceled, not an extra original edge capacity.
  • The supplied network, source 0 and sink 5 are hard-coded; result is 23.
  • For generalized use require s≠t, nonnegative capacities and safe integer totals; matrix input must combine parallel capacities rather than overwrite them.
Checked sample input and observed output

No stdin: the sample data are hard-coded in main.

Observed stdout · GNU C++17 build

The maximum possible flow is 23

This sample was run locally. It verifies the shown case, not every possible input or all stated domain restrictions.

Complete original C++ source · 95 lines
#include <iostream>
#include <limits.h>
#include <queue>
#include <string.h>
using namespace std;

#define V 6


bool bfs(int rGraph[V][V], int s, int t, int parent[])
{

    bool visited[V];
    memset(visited, 0, sizeof(visited));

    queue<int> q;
    q.push(s);
    visited[s] = true;
    parent[s] = -1;

    while (!q.empty()) {
        int u = q.front();
        q.pop();

        for (int v = 0; v < V; v++) {
            if (visited[v] == false && rGraph[u][v] > 0) {
            
                if (v == t) {
                    parent[v] = u;
                    return true;
                }
                q.push(v);
                parent[v] = u;
                visited[v] = true;
            }
        }
    }

    return false;
}

int edmond_karp(int graph[V][V], int s, int t)
{
    int u, v;

    int rGraph[V][V]; // Residual graph where rGraph[i][j]
                   // indicates residual capacity of edge
                   // from i to j (if there is an edge. If
                   // rGraph[i][j] is 0, then there is not)
    for (u = 0; u < V; u++)
        for (v = 0; v < V; v++)
            rGraph[u][v] = graph[u][v];

    int parent[V]; 

    int max_flow = 0; 

    while (bfs(rGraph, s, t, parent)) {
    
        int path_flow = INT_MAX;
        for (v = t; v != s; v = parent[v]) {
            u = parent[v];
            path_flow = min(path_flow, rGraph[u][v]);
        }

        // update residual capacities of the edges and
        // reverse edges along the path
        for (v = t; v != s; v = parent[v]) {
            u = parent[v];
            rGraph[u][v] -= path_flow;
            rGraph[v][u] += path_flow;
        }

        max_flow += path_flow;
    }

    return max_flow;
}


int main()
{
    int graph[V][V]
        = { { 0, 16, 13, 0,  0, 0 }, 
            { 0, 0,  10, 12, 0, 0 },
            { 0, 4,   0, 0, 14, 0 },  
            { 0, 0,   9, 0,  0, 20},
            { 0, 0,   0, 7,  0, 4 }, 
            { 0, 0,   0, 0,  0, 0 } };

    cout << "The maximum possible flow is "
         << edmond_karp(graph, 0, 5);

    return 0;
}

Check your understanding

Why BFS instead of arbitrary DFS?

Shortest augmenting paths give a polynomial bound independent of numeric capacity magnitudes.

How do you obtain a minimum cut?

After termination, find vertices reachable from s using positive residual capacity; edges to the unreachable side form a minimum cut.

What certifies maximum flow?

No residual s–t path remains, and the flow equals the capacity of a corresponding cut.

↑ Back to code index · Choose / compare algorithms

Historical source variant · explicit correction

Historical ZIP variant: fractional knapsack

Historical variant — do not confuse it with the corrected current class file. The ZIP’s fractional branch prints (profit/weight)×100, which is not the selected percentage. For the checked 50-capacity sample it prints 400% for item 3 instead of 66.6667%, although total profit is correctly 240.00. The current class code uses fraction=capacity/weight. Both versions are preserved as supplied.
Observed output demonstrating the bug
Enter number of items: Enter Profit and Weight for each item:
Enter knapsack capacity: 
Selected Items:
Item 1 -> 100%
Item 2 -> 100%
Item 3 -> 400%

Maximum Profit = 240.00
Complete historical ZIP source — known display bug · 71 lines
#include <iostream>
#include <vector>
#include <algorithm>
#include <iomanip>

using namespace std;

struct Item {
    int id;
    double profit;
    double weight;
};

// Comparator function
bool compare(Item a, Item b) {
    return (a.profit / a.weight) > (b.profit / b.weight);
}

int main() {
    int n;
    cout << "Enter number of items: ";
    cin >> n;

    vector<Item> items(n);

    cout << "Enter Profit and Weight for each item:\n";
    for (int i = 0; i < n; i++) {
        items[i].id = i + 1;
        cin >> items[i].profit >> items[i].weight;
    }

    double capacity;
    cout << "Enter knapsack capacity: ";
    cin >> capacity;

    // Sort according to profit/weight ratio
    sort(items.begin(), items.end(), compare);

    double totalProfit = 0.0;

    cout << "\nSelected Items:\n";

    for (int i = 0; i < n; i++) {
        if (capacity == 0)
            break;

        if (items[i].weight <= capacity) {
            // Take the whole item
            cout << "Item " << items[i].id
                 << " -> 100%\n";

            totalProfit += items[i].profit;
            capacity -= items[i].weight;
        }
        else {
            // Take fraction of the item
            double profit_weight_ratio = items[i].profit / items[i].weight;

            cout << "Item " << items[i].id
                 << " -> " << profit_weight_ratio * 100 << "%\n";

            totalProfit += capacity  * profit_weight_ratio;
            capacity = 0;
        }
    }

    cout << fixed << setprecision(2);
    cout << "\nMaximum Profit = " << totalProfit << endl;

    return 0;
}

The ZIP’s dijkstra.cpp, job sequencing.cpp, prims.cpp are exact text duplicates of current files; their full listings already appear above.

Separate assignment reference · not teacher-class attribution

Quicksort assignment: full experiment source

Assignment code, not a new teacher lab listing. This full program is retained so no unique course C++ file is left out. It contains first/last/middle/random pivot strategies, a Lomuto-style partition with pivot moved to high, reproducible input generation, correctness checks and timing/CSV infrastructure.

Key contrast: recurse only into the smaller partition and iterate over the larger one. Sorting still has average/expected O(n log n) and worst O(n²) time, but the sorting routine’s recursion stack is O(log n) even for unbalanced splits. Benchmark datasets and result bookkeeping use additional memory.
  • The class quicksort uses two-pointer pivot placement at low; this assignment uses a one-pass store_index partition at high. Do not mix their tracing rules.
  • Identical inputs, repeat trials, consistent timing boundaries and reproducible seeds make pivot comparisons meaningful; one timing is not an asymptotic proof.
  • C++20 required: unordered_set::contains does not compile under C++17. The unmodified file compiled under C++20 in this update.
  • The benchmark and assignment tests were not executed in this update; they can write CSV files. Only compilation was verified. The original assignment project and its outputs were untouched.
  • The archived assignment copy is byte-identical; it is represented by this single full listing.
Public-copy privacy edit: the original registration-derived random seed is replaced by a neutral seed. No sorting logic is changed. Generated random datasets and benchmark timings will differ; do not compare them as if they used the same original seed.
Complete experiment program · C++20 · seed anonymized · 739 lines
#include <algorithm>
#include <array>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <random>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <unordered_set>
#include <vector>

namespace quicksort {

enum class PivotStrategy : std::uint8_t {
    First = 0,
    Last = 1,
    Middle = 2,
    Random = 3,
};

namespace {

std::size_t choose_pivot_index(
    const std::size_t low,
    const std::size_t high,
    const PivotStrategy strategy,
    std::mt19937_64& random_engine) {
    switch (strategy) {
    case PivotStrategy::First:
        return low;
    case PivotStrategy::Last:
        return high;
    case PivotStrategy::Middle:
        return low + (high - low) / 2U;
    case PivotStrategy::Random: {
        std::uniform_int_distribution<std::size_t> distribution(low, high);
        return distribution(random_engine);
    }
    }
    return low;
}

std::size_t partition(
    std::vector<int>& values,
    const std::size_t low,
    const std::size_t high,
    const PivotStrategy strategy,
    std::mt19937_64& random_engine) {
    const std::size_t pivot_index = choose_pivot_index(low, high, strategy, random_engine);
    std::swap(values[pivot_index], values[high]);
    const int pivot_value = values[high];
    std::size_t store_index = low;

    for (std::size_t index = low; index < high; ++index) {
        if (values[index] <= pivot_value) {
            std::swap(values[index], values[store_index]);
            ++store_index;
        }
    }
    std::swap(values[store_index], values[high]);
    return store_index;
}

void sort_range(
    std::vector<int>& values,
    std::size_t low,
    std::size_t high,
    const PivotStrategy strategy,
    std::mt19937_64& random_engine) {
    while (low < high) {
        const std::size_t pivot_index = partition(values, low, high, strategy, random_engine);
        const std::size_t left_size = pivot_index - low;
        const std::size_t right_size = high - pivot_index;

        // Recurse only into the smaller side, then iterate over the larger side.
        if (left_size < right_size) {
            if (pivot_index > low) {
                sort_range(values, low, pivot_index - 1U, strategy, random_engine);
            }
            low = pivot_index + 1U;
        } else {
            if (pivot_index < high) {
                sort_range(values, pivot_index + 1U, high, strategy, random_engine);
            }
            if (pivot_index == 0U) {
                break;
            }
            high = pivot_index - 1U;
        }
    }
}

} // namespace

void sort(std::vector<int>& values, const PivotStrategy strategy, std::mt19937_64& random_engine) {
    if (values.size() < 2U) {
        return;
    }
    sort_range(values, 0U, values.size() - 1U, strategy, random_engine);
}

} // namespace quicksort

namespace experiment {

enum class InputType : std::uint8_t {
    Random = 0,
    Ascending = 1,
    Descending = 2,
};

struct BenchmarkConfig {
    std::size_t limit{};
    std::size_t step{};
    std::size_t repetitions{};
    std::filesystem::path output_path;
    bool show_progress{};
};

struct ValidationSummary {
    std::size_t row_count{};
    std::size_t duplicate_count{};
    std::size_t zero_duration_count{};
    std::size_t malformed_count{};
};

struct PilotSummary {
    std::size_t measured_runs{};
    std::int64_t elapsed_ns{};
    double estimated_full_seconds{};
};

std::vector<int> make_base_input(InputType input_type, std::size_t size, std::uint64_t seed);
std::array<quicksort::PivotStrategy, 4> rotated_strategy_order(
    std::size_t size_index,
    std::size_t repetition_index,
    InputType input_type);
std::size_t run_benchmark(const BenchmarkConfig& config);
ValidationSummary validate_csv(const std::filesystem::path& csv_path);
std::size_t run_warmup();
PilotSummary run_pilot();

namespace {

constexpr std::uint64_t kMasterSeed = 2372026ULL;
constexpr std::string_view kCsvHeader =
    "Input_Type,Array_Size,Repetition,Strategy,Input_Seed,Pivot_Seed,Run_ID,Order_Position,Duration_ns";

constexpr std::array kStrategies{
    quicksort::PivotStrategy::First,
    quicksort::PivotStrategy::Last,
    quicksort::PivotStrategy::Middle,
    quicksort::PivotStrategy::Random,
};

constexpr std::array kInputTypes{
    InputType::Random,
    InputType::Ascending,
    InputType::Descending,
};

std::string_view input_type_name(const InputType input_type) {
    switch (input_type) {
    case InputType::Random:
        return "Random";
    case InputType::Ascending:
        return "Ascending";
    case InputType::Descending:
        return "Descending";
    }
    return "Unknown";
}

std::string_view strategy_name(const quicksort::PivotStrategy strategy) {
    switch (strategy) {
    case quicksort::PivotStrategy::First:
        return "First";
    case quicksort::PivotStrategy::Last:
        return "Last";
    case quicksort::PivotStrategy::Middle:
        return "Middle";
    case quicksort::PivotStrategy::Random:
        return "Random";
    }
    return "Unknown";
}

std::uint64_t splitmix64(std::uint64_t value) {
    value += 0x9E3779B97F4A7C15ULL;
    value = (value ^ (value >> 30U)) * 0xBF58476D1CE4E5B9ULL;
    value = (value ^ (value >> 27U)) * 0x94D049BB133111EBULL;
    return value ^ (value >> 31U);
}

std::uint64_t derive_seed(
    const InputType input_type,
    const std::size_t size,
    const std::size_t repetition_index,
    const std::uint64_t domain) {
    const auto input_index = static_cast<std::uint64_t>(input_type);
    const auto size_value = static_cast<std::uint64_t>(size);
    const auto repetition_value = static_cast<std::uint64_t>(repetition_index);
    return splitmix64(
        kMasterSeed ^ domain ^ (input_index << 56U) ^ (size_value << 16U) ^ repetition_value);
}

std::string run_key(
    const InputType input_type,
    const std::size_t size,
    const std::size_t repetition,
    const quicksort::PivotStrategy strategy) {
    return std::string(input_type_name(input_type)) + '|' + std::to_string(size) + '|' +
           std::to_string(repetition) + '|' + std::string(strategy_name(strategy));
}

std::vector<std::string> split_csv_line(const std::string& line) {
    std::vector<std::string> fields;
    std::size_t field_start = 0U;
    while (true) {
        const std::size_t comma = line.find(',', field_start);
        if (comma == std::string::npos) {
            fields.push_back(line.substr(field_start));
            break;
        }
        fields.push_back(line.substr(field_start, comma - field_start));
        field_start = comma + 1U;
    }
    return fields;
}

bool parse_unsigned(const std::string& text, std::uint64_t& value) {
    if (text.empty()) {
        return false;
    }
    try {
        std::size_t consumed = 0U;
        value = std::stoull(text, &consumed, 10);
        return consumed == text.size();
    } catch (const std::exception&) {
        return false;
    }
}

struct CsvState {
    ValidationSummary summary;
    std::unordered_set<std::string> completed_keys;
};

CsvState inspect_csv(const std::filesystem::path& csv_path) {
    CsvState state;
    std::ifstream input(csv_path);
    if (!input) {
        throw std::runtime_error("Could not open existing CSV: " + csv_path.string());
    }

    std::string line;
    if (!std::getline(input, line) || line != kCsvHeader) {
        throw std::runtime_error("CSV header does not match the required schema: " + csv_path.string());
    }

    while (std::getline(input, line)) {
        if (line.empty()) {
            ++state.summary.malformed_count;
            continue;
        }

        const auto fields = split_csv_line(line);
        if (fields.size() != 9U) {
            ++state.summary.malformed_count;
            continue;
        }

        std::uint64_t size = 0U;
        std::uint64_t repetition = 0U;
        std::uint64_t input_seed = 0U;
        std::uint64_t pivot_seed = 0U;
        std::uint64_t order_position = 0U;
        std::uint64_t duration = 0U;
        const bool valid_input = fields[0] == "Random" || fields[0] == "Ascending" || fields[0] == "Descending";
        const bool valid_strategy = fields[3] == "First" || fields[3] == "Last" ||
                                    fields[3] == "Middle" || fields[3] == "Random";
        const bool numeric_fields_valid =
            parse_unsigned(fields[1], size) && parse_unsigned(fields[2], repetition) &&
            parse_unsigned(fields[4], input_seed) && parse_unsigned(fields[5], pivot_seed) &&
            parse_unsigned(fields[7], order_position) && parse_unsigned(fields[8], duration);
        if (!valid_input || !valid_strategy || !numeric_fields_valid || fields[6].empty() ||
            size == 0U || repetition == 0U || order_position < 1U || order_position > 4U) {
            ++state.summary.malformed_count;
            continue;
        }

        ++state.summary.row_count;
        if (duration == 0U) {
            ++state.summary.zero_duration_count;
        }
        const std::string key = fields[0] + '|' + fields[1] + '|' + fields[2] + '|' + fields[3];
        if (!state.completed_keys.insert(key).second) {
            ++state.summary.duplicate_count;
        }
    }

    if (!input.eof()) {
        throw std::runtime_error("Error while reading CSV: " + csv_path.string());
    }
    return state;
}

} // namespace

std::vector<int> make_base_input(
    const InputType input_type,
    const std::size_t size,
    const std::uint64_t seed) {
    std::vector<int> values(size);
    std::iota(values.begin(), values.end(), 0);

    if (input_type == InputType::Random) {
        std::mt19937_64 input_engine(seed);
        std::shuffle(values.begin(), values.end(), input_engine);
    } else if (input_type == InputType::Descending) {
        std::reverse(values.begin(), values.end());
    }
    return values;
}

std::array<quicksort::PivotStrategy, 4> rotated_strategy_order(
    const std::size_t size_index,
    const std::size_t repetition_index,
    const InputType input_type) {
    std::array<quicksort::PivotStrategy, 4> order{};
    const std::size_t offset =
        (size_index + repetition_index + static_cast<std::size_t>(input_type)) % kStrategies.size();
    for (std::size_t position = 0U; position < order.size(); ++position) {
        order[position] = kStrategies[(offset + position) % kStrategies.size()];
    }
    return order;
}

std::size_t run_benchmark(const BenchmarkConfig& config) {
    if (config.limit == 0U || config.step == 0U || config.repetitions == 0U ||
        config.limit % config.step != 0U) {
        throw std::invalid_argument("Benchmark limit, step, and repetitions must be positive; limit must be divisible by step.");
    }

    if (!config.output_path.parent_path().empty()) {
        std::filesystem::create_directories(config.output_path.parent_path());
    }

    CsvState existing;
    const bool has_existing_file =
        std::filesystem::exists(config.output_path) && std::filesystem::file_size(config.output_path) > 0U;
    if (has_existing_file) {
        existing = inspect_csv(config.output_path);
        if (existing.summary.malformed_count != 0U || existing.summary.duplicate_count != 0U ||
            existing.summary.zero_duration_count != 0U) {
            throw std::runtime_error("Existing CSV failed integrity checks; refusing to append.");
        }
    }

    std::ofstream output(config.output_path, std::ios::app);
    if (!output) {
        throw std::runtime_error("Could not open CSV for output: " + config.output_path.string());
    }
    if (!has_existing_file) {
        output << kCsvHeader << '\n';
    }

    for (const InputType input_type : kInputTypes) {
        std::size_t size_index = 0U;
        for (std::size_t size = config.step; size <= config.limit; size += config.step, ++size_index) {
            for (std::size_t repetition_index = 0U; repetition_index < config.repetitions; ++repetition_index) {
                const std::uint64_t input_seed = derive_seed(input_type, size, repetition_index, 0x494E505554ULL);
                const auto base_input = make_base_input(input_type, size, input_seed);
                std::array<std::vector<int>, 4> copies{
                    base_input,
                    base_input,
                    base_input,
                    base_input,
                };
                const auto order = rotated_strategy_order(size_index, repetition_index, input_type);

                for (std::size_t order_position = 0U; order_position < order.size(); ++order_position) {
                    const quicksort::PivotStrategy strategy = order[order_position];
                    const std::size_t strategy_index = static_cast<std::size_t>(strategy);
                    const std::size_t repetition = repetition_index + 1U;
                    const std::string key = run_key(input_type, size, repetition, strategy);
                    if (existing.completed_keys.contains(key)) {
                        continue;
                    }

                    const std::uint64_t pivot_seed = derive_seed(
                        input_type,
                        size,
                        repetition_index,
                        0x5049564F54000000ULL ^ static_cast<std::uint64_t>(strategy));
                    std::mt19937_64 pivot_engine(pivot_seed);

                    const auto start = std::chrono::steady_clock::now();
                    quicksort::sort(copies[strategy_index], strategy, pivot_engine);
                    const auto stop = std::chrono::steady_clock::now();

                    if (!std::is_sorted(copies[strategy_index].begin(), copies[strategy_index].end())) {
                        throw std::runtime_error("Post-timing sort validation failed for run " + key);
                    }
                    const auto duration =
                        std::chrono::duration_cast<std::chrono::nanoseconds>(stop - start).count();
                    if (duration <= 0) {
                        throw std::runtime_error("Timer returned a non-positive duration for run " + key);
                    }

                    const std::string run_id = std::string(input_type_name(input_type)) + "_n" +
                                               std::to_string(size) + "_r" + std::to_string(repetition) + '_' +
                                               std::string(strategy_name(strategy));
                    output << input_type_name(input_type) << ',' << size << ',' << repetition << ','
                           << strategy_name(strategy) << ',' << input_seed << ',' << pivot_seed << ','
                           << run_id << ',' << (order_position + 1U) << ',' << duration << '\n';
                    if (!output) {
                        throw std::runtime_error("CSV write failed for run " + key);
                    }
                    existing.completed_keys.insert(key);
                }
                output.flush();
                if (!output) {
                    throw std::runtime_error("CSV checkpoint flush failed: " + config.output_path.string());
                }
            }

            if (config.show_progress && ((size_index + 1U) % 25U == 0U || size == config.limit)) {
                std::cout << input_type_name(input_type) << ": completed through n=" << size
                          << ", total rows=" << existing.completed_keys.size() << '\n';
            }
        }
    }

    return existing.completed_keys.size();
}

ValidationSummary validate_csv(const std::filesystem::path& csv_path) {
    return inspect_csv(csv_path).summary;
}

std::size_t run_warmup() {
    constexpr std::array<std::size_t, 3> warmup_sizes{64U, 256U, 1024U};
    std::size_t completed_runs = 0U;

    for (const InputType input_type : kInputTypes) {
        for (std::size_t size_index = 0U; size_index < warmup_sizes.size(); ++size_index) {
            const std::size_t size = warmup_sizes[size_index];
            const std::uint64_t input_seed = derive_seed(input_type, size, 0U, 0x5741524D5550ULL);
            const auto base_input = make_base_input(input_type, size, input_seed);
            const auto order = rotated_strategy_order(size_index, 0U, input_type);
            for (const quicksort::PivotStrategy strategy : order) {
                auto working_copy = base_input;
                const std::uint64_t pivot_seed = derive_seed(
                    input_type,
                    size,
                    0U,
                    0x5741524D504956ULL ^ static_cast<std::uint64_t>(strategy));
                std::mt19937_64 pivot_engine(pivot_seed);
                quicksort::sort(working_copy, strategy, pivot_engine);
                if (!std::is_sorted(working_copy.begin(), working_copy.end())) {
                    throw std::runtime_error("Warm-up validation failed.");
                }
                ++completed_runs;
            }
        }
    }
    return completed_runs;
}

PilotSummary run_pilot() {
    constexpr std::size_t pilot_size = 5000U;
    constexpr std::size_t pilot_repetitions = 2U;
    std::array<std::array<long double, 4>, 3> duration_totals{};
    std::array<std::array<std::size_t, 4>, 3> duration_counts{};
    PilotSummary summary;
    const auto pilot_start = std::chrono::steady_clock::now();

    for (const InputType input_type : kInputTypes) {
        const std::size_t input_index = static_cast<std::size_t>(input_type);
        for (std::size_t repetition_index = 0U; repetition_index < pilot_repetitions; ++repetition_index) {
            const std::uint64_t input_seed =
                derive_seed(input_type, pilot_size, repetition_index, 0x50494C4F54494EULL);
            const auto base_input = make_base_input(input_type, pilot_size, input_seed);
            std::array<std::vector<int>, 4> copies{base_input, base_input, base_input, base_input};
            const auto order = rotated_strategy_order(499U, repetition_index, input_type);

            for (const quicksort::PivotStrategy strategy : order) {
                const std::size_t strategy_index = static_cast<std::size_t>(strategy);
                const std::uint64_t pivot_seed = derive_seed(
                    input_type,
                    pilot_size,
                    repetition_index,
                    0x50494C4F545049ULL ^ static_cast<std::uint64_t>(strategy));
                std::mt19937_64 pivot_engine(pivot_seed);

                const auto start = std::chrono::steady_clock::now();
                quicksort::sort(copies[strategy_index], strategy, pivot_engine);
                const auto stop = std::chrono::steady_clock::now();

                if (!std::is_sorted(copies[strategy_index].begin(), copies[strategy_index].end())) {
                    throw std::runtime_error("Pilot validation failed.");
                }
                const auto duration =
                    std::chrono::duration_cast<std::chrono::nanoseconds>(stop - start).count();
                if (duration <= 0) {
                    throw std::runtime_error("Pilot timer returned a non-positive duration.");
                }
                duration_totals[input_index][strategy_index] += static_cast<long double>(duration);
                ++duration_counts[input_index][strategy_index];
                ++summary.measured_runs;
            }
        }
    }

    const auto pilot_stop = std::chrono::steady_clock::now();
    summary.elapsed_ns =
        std::chrono::duration_cast<std::chrono::nanoseconds>(pilot_stop - pilot_start).count();

    long double quadratic_weight = 0.0L;
    long double n_log_n_weight = 0.0L;
    const long double maximum_n = static_cast<long double>(pilot_size);
    const long double maximum_n_log_n = maximum_n * std::log2(maximum_n);
    for (std::size_t size = 10U; size <= pilot_size; size += 10U) {
        const long double n = static_cast<long double>(size);
        quadratic_weight += (n * n) / (maximum_n * maximum_n);
        n_log_n_weight += (n * std::log2(n)) / maximum_n_log_n;
    }

    long double estimated_ns = 0.0L;
    for (std::size_t input_index = 0U; input_index < kInputTypes.size(); ++input_index) {
        for (std::size_t strategy_index = 0U; strategy_index < kStrategies.size(); ++strategy_index) {
            const long double mean_at_maximum =
                duration_totals[input_index][strategy_index] /
                static_cast<long double>(duration_counts[input_index][strategy_index]);
            const bool is_monotonic = input_index != static_cast<std::size_t>(InputType::Random);
            const bool is_extreme_pivot =
                strategy_index == static_cast<std::size_t>(quicksort::PivotStrategy::First) ||
                strategy_index == static_cast<std::size_t>(quicksort::PivotStrategy::Last);
            const long double scaling_weight =
                (is_monotonic && is_extreme_pivot) ? quadratic_weight : n_log_n_weight;
            estimated_ns += mean_at_maximum * scaling_weight * 10.0L;
        }
    }
    summary.estimated_full_seconds = static_cast<double>(estimated_ns / 1'000'000'000.0L);
    return summary;
}

} // namespace experiment

namespace {

using quicksort::PivotStrategy;

bool run_correctness_tests() {
    const std::array strategies{
        PivotStrategy::First,
        PivotStrategy::Last,
        PivotStrategy::Middle,
        PivotStrategy::Random,
    };

    const std::vector<std::vector<int>> fixed_cases{
        {},
        {42},
        {1, 2},
        {2, 1},
        {1, 2, 3, 4, 5, 6},
        {6, 5, 4, 3, 2, 1},
        {7, 7, 7, 7, 7},
        {3, 1, 3, 2, 1, 3, 0, 2, 2},
        {0, -10, 5, -3, 5, 2, -10, 9},
    };

    for (const PivotStrategy strategy : strategies) {
        std::uint64_t case_seed = 0x5445535453454544ULL;
        for (const auto& input : fixed_cases) {
            auto actual = input;
            auto expected = input;
            std::sort(expected.begin(), expected.end());
            std::mt19937_64 random_engine(case_seed++);
            quicksort::sort(actual, strategy, random_engine);
            if (actual != expected) {
                std::cerr << "FAIL: fixed correctness case\n";
                return false;
            }
        }

        for (std::uint64_t trial = 0; trial < 8; ++trial) {
            std::mt19937_64 input_engine(0xA110CA7EULL + trial);
            std::uniform_int_distribution<int> value_distribution(-25, 25);
            std::vector<int> actual(257);
            std::generate(actual.begin(), actual.end(), [&] { return value_distribution(input_engine); });
            auto expected = actual;
            std::sort(expected.begin(), expected.end());
            std::mt19937_64 pivot_engine(0xBADC0FFEEULL + trial);
            quicksort::sort(actual, strategy, pivot_engine);
            if (actual != expected) {
                std::cerr << "FAIL: deterministic randomized correctness case\n";
                return false;
            }
        }
    }

    std::cout << "PASS: all 68 correctness cases (17 per strategy)\n";
    return true;
}

bool run_experiment_tests() {
    using experiment::InputType;

    const auto ascending = experiment::make_base_input(InputType::Ascending, 10U, 123U);
    const auto descending = experiment::make_base_input(InputType::Descending, 10U, 123U);
    const auto random_one = experiment::make_base_input(InputType::Random, 10U, 123U);
    const auto random_two = experiment::make_base_input(InputType::Random, 10U, 123U);

    std::vector<int> expected(10U);
    std::iota(expected.begin(), expected.end(), 0);
    auto reversed = expected;
    std::reverse(reversed.begin(), reversed.end());
    auto random_sorted = random_one;
    std::sort(random_sorted.begin(), random_sorted.end());

    if (ascending != expected || descending != reversed || random_sorted != expected || random_one != random_two) {
        std::cerr << "FAIL: controlled input generation\n";
        return false;
    }

    const std::array all_strategies{
        PivotStrategy::First,
        PivotStrategy::Last,
        PivotStrategy::Middle,
        PivotStrategy::Random,
    };
    for (std::size_t position = 0; position < all_strategies.size(); ++position) {
        std::array<bool, 4> seen{};
        for (std::size_t repetition = 0; repetition < all_strategies.size(); ++repetition) {
            const auto order = experiment::rotated_strategy_order(7U, repetition, InputType::Descending);
            const auto strategy_index = static_cast<std::size_t>(order[position]);
            seen[strategy_index] = true;
        }
        if (!std::all_of(seen.begin(), seen.end(), [](const bool value) { return value; })) {
            std::cerr << "FAIL: strategy-order rotation\n";
            return false;
        }
    }

    const auto temporary_path = std::filesystem::temp_directory_path() / "quicksort_benchmark_tdd.csv";
    std::error_code error;
    std::filesystem::remove(temporary_path, error);

    const experiment::BenchmarkConfig config{20U, 10U, 2U, temporary_path, false};
    const std::size_t first_run_rows = experiment::run_benchmark(config);
    const std::uintmax_t first_size = std::filesystem::file_size(temporary_path);
    const std::size_t resumed_rows = experiment::run_benchmark(config);
    const std::uintmax_t resumed_size = std::filesystem::file_size(temporary_path);
    const auto validation = experiment::validate_csv(temporary_path);
    std::filesystem::remove(temporary_path, error);

    if (first_run_rows != 48U || resumed_rows != 48U || first_size != resumed_size ||
        validation.row_count != 48U || validation.duplicate_count != 0U ||
        validation.zero_duration_count != 0U || validation.malformed_count != 0U) {
        std::cerr << "FAIL: checkpoint/resume benchmark CSV\n";
        return false;
    }

    if (experiment::run_warmup() != 36U) {
        std::cerr << "FAIL: unrecorded warm-up\n";
        return false;
    }
    const auto pilot = experiment::run_pilot();
    if (pilot.measured_runs != 24U || pilot.elapsed_ns <= 0 || pilot.estimated_full_seconds <= 0.0) {
        std::cerr << "FAIL: pilot duration estimate\n";
        return false;
    }

    std::cout << "PASS: deterministic generation, rotation, CSV, resume, warm-up, and pilot tests\n";
    return true;
}

} // namespace

int main(int argc, char** argv) {
    try {
        if (argc == 2 && std::string_view(argv[1]) == "--test") {
            return (run_correctness_tests() && run_experiment_tests()) ? 0 : 1;
        }
        if (argc == 2 && std::string_view(argv[1]) == "--warmup") {
            std::cout << "Warm-up complete: " << experiment::run_warmup() << " unrecorded sorts\n";
            return 0;
        }
        if (argc == 2 && std::string_view(argv[1]) == "--pilot") {
            const auto pilot = experiment::run_pilot();
            std::cout << "Pilot runs: " << pilot.measured_runs << '\n'
                      << "Pilot elapsed: " << std::fixed << std::setprecision(3)
                      << (static_cast<double>(pilot.elapsed_ns) / 1'000'000'000.0) << " s\n"
                      << "Estimated full benchmark: " << std::setprecision(1)
                      << pilot.estimated_full_seconds << " s\n";
            return 0;
        }
        if ((argc == 2 || argc == 3) && std::string_view(argv[1]) == "--benchmark") {
            const std::filesystem::path output_path =
                argc == 3 ? std::filesystem::path(argv[2]) : std::filesystem::path("raw_results.csv");
            std::cout << "Warm-up complete: " << experiment::run_warmup() << " unrecorded sorts\n";
            const experiment::BenchmarkConfig config{5000U, 10U, 10U, output_path, true};
            const std::size_t rows = experiment::run_benchmark(config);
            std::cout << "Benchmark complete: " << rows << " unique rows in " << output_path.string() << '\n';
            return rows == 60'000U ? 0 : 1;
        }
        if (argc == 3 && std::string_view(argv[1]) == "--validate-csv") {
            const auto validation = experiment::validate_csv(argv[2]);
            std::cout << "Rows=" << validation.row_count
                      << ", duplicates=" << validation.duplicate_count
                      << ", zero_durations=" << validation.zero_duration_count
                      << ", malformed=" << validation.malformed_count << '\n';
            return validation.duplicate_count == 0U && validation.zero_duration_count == 0U &&
                           validation.malformed_count == 0U
                       ? 0
                       : 1;
        }
    } catch (const std::exception& error) {
        std::cerr << "ERROR: " << error.what() << '\n';
        return 1;
    }
    std::cerr << "Usage:\n"
              << "  quicksort_experiment.exe --test\n"
              << "  quicksort_experiment.exe --warmup\n"
              << "  quicksort_experiment.exe --pilot\n"
              << "  quicksort_experiment.exe --benchmark [raw_results.csv]\n"
              << "  quicksort_experiment.exe --validate-csv raw_results.csv\n";
    return 2;
}
After learning

Revision, formulas & practice

Compact revision aids supplement—not replace—the complete lesson text.

Theory-exam revision · study aid

Second term test · later-course preparation route

Scope warning: “TT-2 / Part-2” is the existing note grouping, not a newly verified official syllabus. The only verified term-test list in the reference folder is explicitly for TT-1; it must not be relabeled TT-2. No topic is hidden or deleted on that basis.

Prepare the second-term/later-course sequence below while retaining earlier prerequisites. If an explicit TT-2 cutoff is announced, use it to prioritize—not to discard the rest of this final-exam master.

Preparation blockIn-file readingProduce without looking
BacktrackingSubset sum, N-Queens, M-coloringState-space tree; promising conditions; include/exclude; safety checks; undo; positive-input assumptions.
Branch & boundClass lesson + labeled extensionsFeasible versus optimal; live/dead nodes; FIFO/LIFO/least-cost; valid optimistic bounds.
Randomization & approximationRandomization, vertex coverLas Vegas/Monte Carlo; expected/worst time; approximation ratio; matching lower bound and 2-approx proof.
Network flowFoundations, max flow, matching, DAG path coverResidual graph, BFS path, bottleneck and reverse-edge update; min-cut certificate; reductions.
Number theory & RSARSA, modular exponentiation, prerequisite supplementGCD/inverse; e versus d; φ versus n; full 9→48→9 calculation; square-and-multiply.
GeometryGift wrapping, Graham, new full codeOrientation; collinear distance tie; hull order; stack pops; O(nh) versus O(n log n).
String matchingNaive, Rabin–Karp, KMPAlignment trace; rolling hash and collision; LPS construction; fallback and overlapping matches.
Complexity classesP / NP / NP-hard / NP-completeDecision formulation, verifier/certificate, reduction direction, what is and is not known about P versus NP.
Graph sheets / foundationsDijkstra & Prim, comparisonsPath versus tree objective, weight assumptions, relaxation, heap behavior and source-component caveat.

Self-test set — generated practice, not predicted questions

  1. For weights [2,3,5,7], target 10: draw include/exclude branches and justify each prune.
  2. Trace one BFS augmentation in the six-vertex network; update every reverse edge as well as forward edges.
  3. For p=11,q=13,e=7: derive d by extended Euclid, then encrypt 9.
  4. Build LPS for ABAB and explain each match in the supplied KMP text.
  5. Compare Graham and Jarvis on the provided 12 points; explain their reversed output order.
  6. Prove the edge-picking vertex-cover 2-approximation and state the direction of a hardness reduction.

Where to check: worked traces, rapid answers, and the original linked lessons/PYQs. For written tests, rehearse from blank paper rather than only recognizing the displayed answer.

Theory-exam revision · study aid

Semester final · full-course mastery checklist

The absence of a C++ file is not an exclusion. Theory derivations, correctness arguments and curriculum-only topics remain essential references. Start with teacher material; use the visibly labeled supplements to fill curriculum gaps without pretending they were lectures.

BlockEvidence labelRead / practice inside this HTMLAcceptance checklist
Foundations & asymptoticsTeacherIntro / TT1 / PYQsDefinition, invariant, input size; O/Ω/Θ; best/average/worst; time and stack space.
RecurrencesTeacherMethods / MasterSubstitution/induction, recursion tree, expansion, regularity; know when Master does not apply.
Greedy & sortingTeacherGreedy / full code / PYQsExchange argument; fractional vs 0–1; job slots; sorting trace, stability and worst-case partitions.
DPTeacher + curriculum/PYQ extensionsDP / PYQs / formula bankState and base cases; transition; fill order; traceback; coin variants; LCS/LIS/LDS; matrix chain.
Backtracking / branch & boundTeacher + extensionsCore / extensions / PYQsPruning soundness, N-Queens, coloring, Hamiltonian cycle, 15-puzzle/TSP bounds as labeled.
Randomization / approximationTeacherRandomized / approximationProbability versus correctness; expected time; exact vs approximation; feasibility and ratio proof.
Graphs, shortest paths & MSTTeacher + supportSheets / support / PYQsTraversal, connectivity, relaxation, cut/cycle properties; distinguish directed and undirected assumptions.
Network flow / matching / coverTeacherFlow / matching / PYQsCapacity/conservation; residual updates; max-flow/min-cut; bipartite reduction; path/edge/vertex cover distinctions.
GeometryTeacher + supplementHull / segment / closest pair / PYQsCross product, degeneracy, segment intersection cases, closest-pair recurrence and sorting discipline.
Number theory / RSATeacher + supplementRSA / CRT, φ, EEA / PYQsCongruence, GCD, inverse, primality/sieve, factorization, CRT compatibility, key derivation and modular powers.
P / NP / hardnessTeacherDefinitions / PYQsDecision language, certificate, membership proof and reduction direction; no assumed P≠NP.
String matching & structuresTeacher + supplementMatching / trie / suffix / automaton / PYQsNaive/RK/KMP traces; hash collisions; LPS; prefix vs suffix indexing and query operations.
Hash tablesCurriculum supplementLesson / PYQsLoad factor; collisions; chaining vs open addressing; probe cycles, deletion markers and assumptions behind expected O(1).
Red-black / binomial / StrassenCurriculum supplementTrees/heaps / Strassen / PYQsRB properties and repair cases; binomial-tree linking; seven subproducts and recurrence.
Combinatorics / probability / gamesCurriculum supplementLessonCounting assumptions, expectation, winning/losing states and applicable game rules.
LCA / RMQCurriculum supplementLessonBinary lifting, Euler tour, sparse table, preprocess/query complexity and static-versus-updatable data.
Polynomials / DFT / FFTCurriculum supplementLessonCoefficient vs value representation, roots of unity, even/odd split, convolution and padding.

Answer templates for a written exam

Question asks…Write…
Explain an algorithmProblem + assumptions → state/data structure → steps/pseudocode → small example → correctness → time/space.
Design a DPState meaning → base cases → transition with all choices → evaluation order → answer location → reconstruction → complexity.
Prove greedy correctnessDefine the greedy choice → exchange or cut argument → show a compatible optimum exists → apply to the remaining subproblem.
Analyze recurrenceState recurrence and base case → name a valid method → show its decisive calculation → tight bound; do not just name the theorem.
Trace code / algorithmDeclare indexing and tie rules → record changing state at each step → give final output → note unsupported inputs if relevant.
Prove NP-completenessDecision version → polynomial verifier → known NP-complete source A → polynomial mapping A≤pB → both directions of equivalence.
Compare alternativesSame objective and input model → assumptions → correctness/quality guarantee → time/space → a case where the distinction matters.

Blank-paper readiness test

For each checklist row: define it, derive its main recurrence/transition, trace one example, justify correctness, state complexity, and answer one edge-case question. If one step fails, return to that lesson and redo a changed example. A remembered sample output is not sufficient mastery.

Mixed practice: take questions across at least analysis, greedy/DP, search/graphs, number theory and strings/complexity; leave time to check assumptions and notation. The existing 254 PYQ cards remain available with answers and source scans.

Theory-exam revision · study aid

Final-exam formula & recurrence bank

Revision formulas collected for the written exams. These do not replace the full derivations below. LIS/LDS, matrix-chain and other curriculum extensions are supplemental wherever direct class evidence is absent.

TopicCore recurrence / factConditions and complexity
0–1 knapsackK[i,w]=max(K[i−1,w], pᵢ+K[i−1,w−wᵢ]) when wᵢ≤w; otherwise skip.Empty prefix is 0; each item once. O(nW) time, O(nW) full traceback table.
Minimum unbounded coinsC[i,a]=min(C[i−1,a], 1+C[i,a−cᵢ]) when cᵢ≤a.C[i,0]=0; impossible positive amounts = INF; positive denominations. O(nA).
Number of coin combinationsways[0]=1; for each positive coin c, for a=c..A: ways[a]+=ways[a−c].Unbounded, order ignored, distinct denomination types. Swapping loop order typically counts ordered sequences.
LCSEqual last characters: L[i,j]=1+L[i−1,j−1]; else max(L[i−1,j],L[i,j−1]).Zero row/column. O(nm). Subsequence, not substring.
LIS / LDSLIS[i]=1+max(LIS[j]) over jStrict inequalities for strictly monotone sequences; <= gives nondecreasing. O(n²) DP; O(n log n) tails method needs reconstruction bookkeeping.
Matrix-chain multiplicationM[i,j]=min over i≤kMatrix i is pᵢ₋₁×pᵢ. M[i,i]=0; fill increasing chain length. O(n³) time, O(n²) space; store k to reconstruct parentheses.
Count all parenthesizationsP[1]=1; P[n]=Σ(k=1..n−1)P[k]P[n−k]=Catalan(n−1).Counts all full parenthesizations, NOT only minimum-cost ones; for four matrices the count is 5.
Boolean subset sumS[i,t]=S[i−1,t] OR S[i−1,t−aᵢ] when aᵢ≤t.S[i,0]=true; nonnegative elements/target. O(nT), pseudo-polynomial.
Minimum DAG vertex-disjoint path cover|V|−|maximum matching| in the split-vertex bipartite graph.Only for DAGs under this construction; path cover is not vertex cover.
Minimum edge cover|V|−|maximum matching|.Undirected graph with no isolated vertices; isolated vertices prevent an edge cover of every vertex.
Euclid / inversegcd(a,b)=gcd(b,a mod b); ax+my=gcd(a,m).If gcd=1, inverse is ((x mod m)+m) mod m.
Euler φφ(n)=n ∏(p divides n)(1−1/p), over distinct prime factors.For prime p, φ(p)=p−1. For distinct p,q, φ(pq)=(p−1)(q−1).
Generalized CRT compatibilityx≡a mod m and x≡b mod n are compatible iff gcd(m,n) divides b−a.When compatible, solution is unique modulo lcm(m,n); pairwise coprime moduli are the familiar simpler case.
RSAn=pq; ed≡1 mod φ(n); c=mᵉ mod n; m=cᵈ mod n.Exponent inverse uses φ(n); encryption/decryption modulus is n. Distinct primes; message represented in 0..n−1.
Strassen / FFTStrassen: T(n)=7T(n/2)+Θ(n²)=Θ(n^(log₂7)). FFT: 2T(n/2)+Θ(n)=Θ(n log n).Strassen partitions matrices; FFT separates even/odd coefficients. Padding and arithmetic model matter.

Two small extension examples

LIS: [3,1,2,5,4] gives per-position lengths [1,1,2,3,3]; one LIS is [1,2,5]. Repeated values do not extend a strictly increasing sequence.

Matrix chain: A₁=10×30, A₂=30×5, A₃=5×60. (A₁A₂)A₃ costs 10·30·5 + 10·5·60 = 4500. A₁(A₂A₃) costs 30·5·60 + 10·30·60 = 27000. The matrices keep their order; only parentheses change.

Counting optimal parenthesizations: maintain a count beside the minimum cost. A lower cost replaces both cost and count; an equal cost adds count(left)×count(right). The Catalan count above is for all parenthesizations regardless of cost.

Theory-exam revision · study aid

Algorithm selection, complexity & tracing

Symbols: n,m = relevant input lengths/counts; V,E = vertices/edges; W = capacity; A = amount; T = target; D = maximum deadline; h = hull vertices. Bounds refer to the supplied implementation where noted.

Question clueChooseKey distinctionTime
Divisible items, capacityFractional knapsackProfit/weight greedy; fractions allowed.O(n log n)
Indivisible items, max profit0–1 knapsackPrevious DP row for taking an item.O(nW), pseudo-polynomial
Fewest reusable coinsMinimum coin changeCurrent DP row for taking a coin.O(nA)
Existence of target subsetSubset-sum DPBoolean OR; one reconstructed subset.O(nT)
List target subsetsSubset backtrackingInclude/exclude with nonnegative-sum pruning.Exponential + output
Longest common ordered sequenceLCSSubsequence may skip characters.O(nm)
Every exact substring matchKMPLPS fallback; supports overlaps.O(n+m)
Shortest paths, nonnegative edgesDijkstradist[u]+w relaxation; directed or undirected.Heap: O(V+E log(E+1))
Minimum total connecting treePrimEdge weight w relaxation; undirected, connected.Heap: O(V+E log(E+1))
Unit jobs with deadlinesJob sequencingProfit descending; latest free slot.O(n log n+nD+D) here
Stable comparison sortingMerge sortLeft wins equality.Θ(n log n); O(n) extra
In-place partition sortingQuicksortBalanced partitions matter.Average Θ(n log n), worst Θ(n²)
Planar hull, general nGraham ScanAngle sort then left-turn stack.O(n log n)
Planar hull, small hJarvis MarchOne full scan per hull vertex.O(nh)
Maximum flowEdmonds–KarpResidual BFS + bottleneck augmentation.List O(VE²); supplied matrix O(V³E)
Nonattacking queens / proper coloringN-Queens / M-coloringAssign → check → recurse → undo.Exponential search

High-value traps

  • Dijkstra ≠ Prim: path distance versus one crossing edge; Prim allows negative weights.
  • 0–1 ≠ unbounded: previous row prevents reuse; current row permits it. In 1D, descending capacity is 0–1, ascending is unbounded.
  • LCS ≠ substring: LCS can skip; KMP/Rabin–Karp search contiguous matches.
  • O ≠ worst case: O is an upper-bound notation; best/average/worst are separate input-case choices.
  • Heap duplicates ≠ an error: these graph codes use lazy insertion and a visited guard.
  • Pseudo-polynomial ≠ polynomial in input bits: O(nW) depends on the value W, not log W.
  • Compiled ≠ correct for every input: read the domain restrictions and known defects next to each original listing.

Write a trace, not a guess

AlgorithmColumns / state to record
Quicksortlow, high, pivot value, i, j, swaps, final j; then subranges.
DPMeaning of dp[i][j], base row/column, skip/take candidate, chosen value, reconstruction path.
Dijkstra / PrimExtracted vertex, finalized set, dist/key vector, changed parent and heap insertion.
BacktrackingLevel, candidate, partial solution, rejection reason; reset state before siblings.
Graham / JarvisCross-product sign, stack/candidate before and after, collinear distance tie.
KMPi, j, compared characters, LPS fallback, match index i−m.
FlowBFS parent path, bottleneck, forward decrement, reverse increment, accumulated flow.
Theory-exam revision · study aid

Worked traces & output checks

Small worked traces for recall. Full compiled-program outputs are attached to the corresponding code cards.

KMP: overlap and fallback

PatternLPSTextZero-based matches
ABAB0 0 1 2ABABDABACDABABCABAB0, 10, 15 (supplied program)
AAA0 1 2AAAAA0, 1, 2 (revision example)

After matching ABAB at text indices 0..3, i=4 and j falls from 4 to lps[3]=2. Text[4]=D mismatches pattern[2]=A, so j→lps[1]=0; D still mismatches A, so i advances. The text index never moves backward.

0–1 versus fractional knapsack

Items (weight,profit)=(10,60),(20,100),(30,120), capacity 50. Fractional: first two fully plus 20/30 of the third ⇒ 240. 0–1: second and third ⇒ 220. At dp[3][50], skip=160; take=120+dp[2][20]=220.

Subset DP versus enumeration

For [2,3,5,7], target 10, backtracking returns {2,3,5} and {3,7}. Boolean DP only stores whether a sum is reachable and normally reconstructs one solution. The DP can merge different paths reaching the same state.

Residual-flow trace: supplied six-vertex network

BFS pathBottleneckAccumulated flow
0→1→3→51212
0→2→4→5416
0→2→4→3→5723

Every path update decreases forward residuals and increases reverse residuals. At the end, residual-reachable vertices are {0,1,2,4}; original cut edges 1→3 (12), 4→3 (7), 4→5 (4) sum to 23.

Hull sample: same polygon, reverse orders

Graham: (0,0)→(4,0)→(5,2)→(4,4)→(2,5)→(0,4). Jarvis: (0,0)→(0,4)→(2,5)→(4,4)→(5,2)→(4,0). Both omit all six supplied interior points. This is traversal direction, not disagreement about the hull.

Quick partition: supplied nine numbers

[50,70,60,90,40,80,10,20,30], pivot=50. Swap 70↔30, then 60↔20, then 90↔10. Pointers cross at i=5,j=4; swap pivot 50↔40. First partition becomes [40,30,20,10,50,80,90,60,70], pivot index 4. Recurse on 0..3 and 5..8.

Theory-exam revision · study aid

Concept questions · course-wide rapid answers

59 concept questions across the taught theory, plus algorithm-specific checks beside every class listing. These are revision prompts, not predicted exam questions.

Answer structure: definition → state/invariant → essential step → correctness reason → time/space → one limitation. Close the answer and reproduce the reasoning before checking.

Foundations, analysis & recurrences

Open original course explanation ↗

What makes an algorithm valid?

Definite executable steps, input/output specification, finiteness and correctness for every valid input.

How do you prove a loop correct?

State an invariant; prove initialization and maintenance; use termination to establish the required result. Also justify that it terminates.

O, Ω and Θ?

O is an asymptotic upper bound; Ω a lower bound; Θ both. They are not synonyms for worst, best and average cases.

Order common growth rates.

For large n: 1 < log n < n < n log n < n² < n³ < 2ⁿ < n!.

Binary search recurrence?

T(n)=T(n/2)+Θ(1)=Θ(log n); the array must be sorted. Iterative space O(1), recursive stack O(log n).

Merge sort recurrence?

T(n)=2T(n/2)+Θ(n)=Θ(n log n).

Quicksort best and worst recurrences?

Balanced: 2T(n/2)+Θ(n)=Θ(n log n). One-sided: T(n−1)+Θ(n)=Θ(n²).

Master theorem conditions and cases?

For T(n)=aT(n/b)+f(n), a≥1, b>1: compare f(n) with n^(log_b a). Polynomially smaller gives Θ(n^(log_b a)); equal order gives an extra log; polynomially larger with af(n/b)≤c f(n), c<1, gives Θ(f(n)). Do not apply it to T(n−1).

Solve 3T(n/4)+n².

n² dominates n^(log_4 3); regularity ratio is 3/16<1, so Θ(n²).

Solve T(n−1)+log n.

Sum log k for k=2..n: log(n!)=Θ(n log n).

Why is naive recursive Fibonacci slow?

It repeats overlapping subproblems. DP reduces the work to Θ(n); keeping only the previous two values uses O(1) auxiliary space, ignoring large-integer bit cost.

Space complexity versus auxiliary space?

Total space includes input/output storage; auxiliary space is additional working memory. State which convention you use and include the recursion stack.

Greedy, divide-and-conquer & DP

Open original course explanation ↗

Greedy versus DP?

Greedy commits to a locally best choice backed by an exchange/cut argument. DP compares alternatives over reusable subproblems.

Divide-and-conquer versus DP?

Divide-and-conquer recursively solves smaller subproblems; DP specifically exploits repeated/overlapping subproblems and optimal substructure.

Optimal substructure?

An optimal solution can be assembled from optimal solutions to the relevant subproblems. This alone does not justify a greedy choice.

Memoization versus tabulation?

Memoization is top-down caching of reached states; tabulation is bottom-up evaluation in dependency order. Both need correct state and base cases.

A counterexample to 0–1 ratio greedy?

Capacity 50, (weight,profit)=(10,60),(20,100),(30,120). Ratio greedy picks the first two for 160; the last two give 220.

What does stable sorting mean?

Equal-key records retain their original relative order. The supplied merge uses <= and is stable; the supplied quicksort is not.

Why does a DP traceback sometimes return a different answer?

Different tie-breaking can return different optimal solutions with the same value. Check objective value and validity, not just one expected sequence.

Coin change minimum-count versus counting ways?

Minimum-count uses min plus one with INF for impossible states. Counting ways uses addition with a count-one empty construction; loop order can distinguish combinations from permutations.

Can 0–1 knapsack be NP-hard and have O(nW) DP?

Yes. O(nW) is pseudo-polynomial because W may be exponentially large in its bit length.

Backtracking, bounds, randomization & approximation

Open original course explanation ↗

Backtracking versus branch and bound?

Backtracking prunes infeasible partial solutions; branch and bound also prunes states whose objective bound cannot improve the best known solution.

What is a promising state?

A partial solution not yet ruled out by constraints or a valid objective bound; being promising does not guarantee success.

FIFO, LIFO and least-cost branch and bound?

FIFO uses a queue (breadth-first), LIFO a stack (depth-first), least-cost a priority queue ordered by a suitable bound. The container alone is not the bounding argument.

What makes a bound safe?

For maximization, an optimistic upper bound must not underestimate achievable profit. For minimization, a lower bound must not overestimate achievable cost.

Why reset choices when backtracking?

Sibling branches must start from the same parent state; stale choices can invalidate later solutions.

Monte Carlo versus Las Vegas?

Monte Carlo can have bounded error probability; Las Vegas always returns a correct answer when it terminates, with randomized running time.

Define approximation ratio.

For minimization, cost(A)≤ρ·OPT; for maximization, value(A)≥OPT/ρ for ρ≥1 under the relevant positive-objective assumptions.

Why is the edge-picking vertex-cover algorithm a 2-approximation?

The picked disjoint edges form a matching; any cover needs at least one endpoint per matched edge. Taking both endpoints yields at most twice optimum.

Maximal versus maximum matching?

Maximal cannot be extended by adding another edge; maximum has the largest possible cardinality. A maximal matching need not be maximum.

Graph algorithms & network flow

Open original course explanation ↗

BFS versus DFS?

BFS explores layers and finds fewest-edge paths in unweighted graphs; DFS explores deeply and supports structural tasks. With adjacency lists both are O(V+E).

Shortest path versus spanning tree?

Shortest path minimizes distance between chosen endpoints; MST minimizes the sum of edges needed to connect all vertices.

Flow constraints?

Capacity: 0≤f(u,v)≤c(u,v) in the nonnegative edge-flow formulation. Conservation: total inflow equals outflow at every vertex except source and sink.

Residual capacity?

It records how much an existing flow can be increased on a forward edge or canceled through a reverse edge.

Why take the minimum capacity on a path?

The smallest residual edge is the bottleneck; sending more would violate its capacity.

Ford–Fulkerson versus Edmonds–Karp?

Ford–Fulkerson is the augmenting-path method; Edmonds–Karp specifically uses BFS shortest augmenting paths and has a polynomial bound.

Max-flow/min-cut theorem?

The maximum feasible s–t flow value equals the minimum s–t cut capacity.

Reduce bipartite matching to max flow.

Add s→left, left→right for allowed pairs, and right→t, all with capacity 1. Integral max flow picks nonconflicting pairs.

Minimum vertex-disjoint path cover of a DAG?

Split each vertex into left/right copies; connect left u to right v for each DAG edge u→v. Answer is |V|−maximum matching size. The DAG and vertex-disjoint conditions matter.

MST uniqueness?

Distinct edge weights are sufficient, not necessary, for uniqueness. Equal weights can allow several MSTs with the same total weight.

Number theory & RSA

Open original course explanation ↗

Euclid’s recurrence?

gcd(a,b)=gcd(b,a mod b), ending at b=0. It takes O(log min(a,b)) arithmetic steps for positive inputs.

What does extended Euclid return?

g=gcd(a,b) plus x,y satisfying ax+by=g. If g=1, x reduced modulo b is the inverse of a modulo b.

When does a modular inverse exist?

a has an inverse modulo m exactly when gcd(a,m)=1, for m>1.

Euler’s totient?

φ(n) counts residues from 1..n coprime to n. For distinct primes p,q, φ(pq)=(p−1)(q−1).

RSA key generation?

Choose distinct primes p,q; n=pq; choose e coprime to φ(n); find d with ed≡1 mod φ(n). Public key (e,n); private exponent d.

Class RSA example?

p=11,q=13 ⇒ n=143,φ=120. e=7,d=103 since 7×103=721≡1 mod120. M=9 encrypts to C=48; 48^103 mod143=9.

Fast modular exponentiation?

Repeatedly square the base, multiply it into the answer when the current exponent bit is 1, and shift the exponent. O(log exponent) modular multiplications.

Does textbook RSA describe production security?

No. Tiny classroom primes and raw deterministic RSA are teaching examples. Real systems require approved padding and implementation protections; do not use this demo for secrets.

Euler theorem condition?

If gcd(a,n)=1 then a^φ(n)≡1 mod n. Do not omit the coprimality condition.

Geometry, strings & complexity classes

Open original course explanation ↗

Orientation formula?

cross=(bx−ax)(cy−ay)−(by−ay)(cx−ax): positive is CCW, negative CW, zero collinear under ordinary Cartesian axes.

Why can coordinate arithmetic overflow?

Differences and products can exceed their types. Widen before subtracting; even 64-bit products need coordinate bounds.

Naive substring matching complexity?

Try n−m+1 alignments and compare at most m characters each: O(nm) worst case for n≥m.

Rabin–Karp collision handling?

Equal hashes are candidates, not proof of equal strings. Verify characters. With suitable hashing expected time is near O(n+m), worst O(nm).

Rolling-hash update?

Remove the old leading character’s weighted contribution, multiply by the base, add the new trailing character, and normalize modulo q.

P versus NP?

P: decision problems solvable in deterministic polynomial time. NP: yes-instances have certificates verifiable in polynomial time. P⊆NP; whether P=NP is unknown.

NP-hard versus NP-complete?

NP-hard means every problem in NP reduces to it in polynomial time. NP-complete additionally requires membership in NP. NP-hard problems need not be decision problems or even decidable.

How to prove a new problem B NP-complete?

Show B∈NP, then reduce a known NP-complete A TO B. Reducing B to A is the wrong direction for proving B hard.

Does NP mean non-polynomial?

No. It means nondeterministic polynomial time; polynomial-time problems are also in NP.

Decision versus optimization?

Decision asks yes/no for a threshold; optimization asks for the best value/solution. State the problem version before assigning a complexity class.

Five-minute blank-paper check

  1. Pick one listing without opening its explanation. State its problem and input assumptions.
  2. Explain every state variable and the loop/recursion invariant.
  3. Dry-run a tiny example and one boundary case.
  4. State the exact implementation’s time and auxiliary space; identify its bottleneck.
  5. Answer a “why not another algorithm?” comparison.

Course-wide coverage: the curriculum supplement sections (hashing, red-black trees, binomial heaps, Strassen, CRT, tries, suffix structures, LCA/RMQ, FFT and others) remain below. Their “supplement” label distinguishes them from directly evidenced classroom teaching.

Official curriculum coverage map

The official curriculum includes substantially more than the handwritten notebooks. Missing classroom derivations are retained as visibly labeled curriculum supplements, not presented as teacher lectures.

AreaStatusWhere covered
Analysis of algorithmsTeacher taughtTT1 pp.1–15 + intro slides
Hash tablesCurriculum supplementHash supplement
Greedy algorithmsTeacher taughtTT1 pp.17–21 + Dijkstra sheet
Dynamic programmingTeacher taughtTT1 pp.32–40
Red-black treeCurriculum supplementRB/Binomial supplement
Binomial heapsCurriculum supplementRB/Binomial supplement
Strassen’s algorithmCurriculum supplementStrassen supplement
Network flowTeacher taughtTT2/Part-2 flow lessons
Backtracking / Branch-and-BoundTeacher taughtTT1 pp.41–44 + TT2 pp.1–5,12
Geometric algorithmsTeacher taught + supplementGift Wrapping & Graham; segment/closest-pair supplement
Number theory / RSATeacher taught + supplementRSA/mod-exp + CRT/Euclid/φ supplement
NP / NP-hard / NP-completeTeacher taughtLate note pp.15–16
String matchingTeacher taught + supplementNaïve/Rabin–Karp/KMP; automata/trie/suffix supplement
Basic combinatorics / probability / game theoryCurriculum supplementDedicated supplement
LCA / RMQCurriculum/PYQ supplementDedicated supplement
Polynomials / DFT / FFTCurriculum supplementDedicated supplement

Coverage audit: every curriculum family listed in Course curriculum is represented somewhere in this file.

Official curriculum

Verified supplements

These are standard course explanations for curriculum items without a matching full handwritten classroom derivation. They are deliberately not labeled “teacher taught”.

Verified curriculum supplement

Hash Tables

Official curriculum topic; no matching full handwritten classroom derivation was found in the supplied notes.

Curriculum supplement

Purpose

A hash table maps a key to an array index using a hash function, targeting expected O(1) search/insert/delete under good distribution and controlled load factor.

Collision handling

  • Chaining: each bucket stores a collection of colliding keys.
  • Open addressing: all keys stay in the table; probe alternative slots. Linear probing: h(k,i)=(h(k)+i) mod m.
  • Double/multi probing: use another step function to reduce primary clustering.
  • Perfect hashing: for a static key set, arrange collision-free lookup with O(1) worst-case lookup.

A good non-cryptographic hash should be deterministic, fast, distribute expected keys approximately uniformly, and use the key information so small patterns do not cluster heavily.

Verified curriculum supplement

Red-Black Trees & Binomial Heaps

Official curriculum topic; no matching full handwritten classroom derivation was found in the supplied notes.

Curriculum supplement

Red-black tree

A self-balancing BST with a color bit and invariants: root black; NIL leaves black; red node has black children; every node-to-descendant-NIL path has the same black height. These imply height O(log n), so search/insert/delete are O(log n). Rotations plus recoloring restore properties.

Binomial heap

A forest of binomial trees with at most one tree of each degree. Trees are linked like binary carries. Merge/union is the central operation; common bounds are O(log n) for minimum extraction and union, with efficient insertion depending on representation.

Verified curriculum supplement

Strassen’s Matrix Multiplication

Official curriculum topic; no matching full handwritten classroom derivation was found in the supplied notes.

Curriculum supplement

Split each matrix into four n/2 × n/2 blocks. Ordinary divide-and-conquer needs 8 recursive block multiplications; Strassen algebraically reduces this to 7, plus O(n²) additions/subtractions.

T(n)=7T(n/2)+Θ(n²)=Θ(nlog₂7)≈Θ(n2.807)

Useful asymptotically for sufficiently large dense matrices; constants, numerical issues and memory overhead matter in practice.

Verified curriculum supplement

Hamiltonian Cycle, 15-Puzzle & TSP Branch-and-Bound

Official curriculum topic; no matching full handwritten classroom derivation was found in the supplied notes.

Curriculum supplement

Hamiltonian cycle

Backtrack through unused adjacent vertices; accept when all vertices are used exactly once and the last connects back to the start.

15-puzzle / N-puzzle

State = board arrangement; branch = legal blank moves; bound/heuristic often uses misplaced tiles or Manhattan distance. A best-first branch-and-bound/A*-style search prioritizes smaller estimated total cost.

TSP Branch-and-Bound

Branch on partial tours. Compute a lower bound on every unfinished tour. Prune a branch when its lower bound is no better than the best complete tour found.

Verified curriculum supplement

Segment Intersection & Closest Pair

Official curriculum topic; no matching full handwritten classroom derivation was found in the supplied notes.

Curriculum supplement

Orientation

cross = (B−A)×(C−A)

Sign tells counter-clockwise / clockwise / collinear. General segment intersection uses four orientation tests plus collinear-on-segment special cases.

Closest pair

Divide points by x-coordinate, recursively solve halves, then inspect only a narrow strip around the divider ordered by y. Standard divide-and-conquer runs in O(n log n).

Verified curriculum supplement

CRT, Euler φ, Extended Euclid & Prime-Factorization Tools

Official curriculum topic; no matching full handwritten classroom derivation was found in the supplied notes.

Curriculum supplement

Euclid / Extended Euclid

Euclid repeatedly replaces (a,b) by (b,a mod b). Extended Euclid additionally finds x,y with ax+by=gcd(a,b). If gcd(a,m)=1, x mod m is the inverse of a modulo m.

Euler φ

φ(n) counts 1≤k≤n coprime to n. If n=∏pᵢaᵢ, then φ(n)=n∏(1−1/pᵢ). For distinct primes p,q: φ(pq)=(p−1)(q−1).

Chinese Remainder Theorem

For pairwise-coprime moduli mᵢ, the simultaneous congruences x≡aᵢ (mod mᵢ) have a unique solution modulo M=∏mᵢ.

Prime factorization application

Knowing prime factors makes φ(n) easy to compute; RSA deliberately chooses n=pq so the owner knows the factors while an attacker sees only n.

Verified curriculum supplement

Finite-Automata Matching, Trie, Suffix Tree & Suffix Array

Official curriculum topic; no matching full handwritten classroom derivation was found in the supplied notes.

Curriculum supplement

Finite automaton matching

Precompute a transition function δ(state, character) where state is the number of pattern characters matched. Then scan the text once; reaching state m reports a match.

Trie

Prefix tree: each edge is a character. Insert/search a word of length L in O(L) when alphabet operations are O(1).

Suffix tree / suffix array

A suffix tree is a compressed trie of all suffixes; a suffix array stores suffix starting indices in lexicographic order. Suffix arrays use less memory and support pattern search via binary search plus LCP enhancements.

Verified curriculum supplement

Basic Combinatorics, Probability & Game Theory

Official curriculum topic; no matching full handwritten classroom derivation was found in the supplied notes.

Curriculum supplement

Combinatorics

Core counting rules: product rule; permutations n!; combinations C(n,r)=n!/(r!(n−r)!). Useful for reasoning about state-space size.

Probability

Probability measures event likelihood; expected value is E[X]=Σx·Pr[X=x]. Randomized-algorithm analysis often studies expected running time or error probability.

Game theory

Model players, strategies and payoffs. Algorithmic problems often ask for optimal strategies/equilibria under stated assumptions.

Verified curriculum supplement

Least Common Ancestor & Range Minimum Query

Official curriculum topic; no matching full handwritten classroom derivation was found in the supplied notes.

Curriculum supplement

LCA

The lowest common ancestor of u and v is their deepest common ancestor in a rooted tree. Binary lifting preprocesses 2k-ancestors in O(n log n) and answers LCA in O(log n).

RMQ / Sparse table

For a static array, precompute minimums for intervals of length 2k. Build in O(n log n), query minimum on [L,R] in O(1) using two overlapping power-of-two blocks.

Verified curriculum supplement

Polynomials, DFT & FFT

Official curriculum topic; no matching full handwritten classroom derivation was found in the supplied notes.

Curriculum supplement

Represent a degree-(n−1) polynomial by coefficients or by values at n points. Polynomial multiplication by coefficients is convolution.

DFT

Evaluate coefficients at roots of unity; inverse DFT recovers coefficients.

FFT

Exploit even/odd coefficient decomposition recursively, reducing DFT from O(n²) to O(n log n). Polynomial multiplication becomes: FFT both coefficient arrays → pointwise multiply → inverse FFT.

Verified curriculum supplement

Graph Traversal, Connectivity & MST Context

Official curriculum topic; no matching full handwritten classroom derivation was found in the supplied notes.

Curriculum supplement

The PYQ bank includes BFS/DFS, topological ordering, bridges/articulation points, SCCs, Euler paths, Bellman–Ford and MST properties. Treat these as PYQ-supported graph context unless your teacher separately covered them.

  • BFS: level order, O(V+E).
  • DFS: depth-first recursion/stack, O(V+E).
  • Bellman–Ford: relax all edges V−1 times; handles negative edges and detects reachable negative cycles, O(VE).
  • MST cut principle: a lightest edge crossing a cut is safe for some MST; Prim grows one tree.
Exam bank

PYQ map & exam-ready answers

Complete teacher-linked and curriculum-linked coverage of the supplied PYQ bank. Questions stay visible; answers start collapsed.

Complete previous-year question map

Asymptotic Analysis & Recurrences

All bank questions for this topic are represented. Question stays visible; answer stays collapsed. Clear duplicates are consolidated while every occurrence and alternate wording is retained.

PYQ56 bank questions
Q1
Graphically define the notations O(n), Ω(n) and Θ(n).
2 marks · CSE 16-17, Final; CSE 17, Final · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplementExam answer: O(g(n)) is an asymptotic upper bound: 0≤f(n)≤cg(n) for n≥n₀. Ω(g(n)) is a lower bound: f(n)≥cg(n). Θ(g(n)) is a tight bound: c₁g(n)≤f(n)≤c₂g(n). Graphically, beyond n₀, f lies below cg for O, above cg for Ω, and between c₁g and c₂g for Θ.
Q2
Define Big-θ and Big-Ω and explain graphically.
2 marks · 2017-18 Akash · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Big-Θ is a tight two-sided bound; Big-Ω is a lower bound. For sufficiently large n: Θ means c₁g(n)≤f(n)≤c₂g(n), while Ω means f(n)≥c·g(n).
Q3
What does Big-O and Big-Ω signifies in an algorithm? Write the pseudocode of an algorithm where the Big-O and Big-Ω are 2 different functions.
5 marks · CSE 19, Final · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Big-O gives an asymptotic upper bound; Big-Ω gives an asymptotic lower bound. A single algorithm can have different best- and worst-case growth.
Derivation / working
Example — linear search:
LinearSearch(A,n,key):
  for i = 0..n-1:
    if A[i] == key: return i
  return -1

Best case: key is at A[0] ⇒ one comparison ⇒ Ω(1). Worst case: key is last/absent ⇒ n comparisons ⇒ O(n). Formally, for large n, O(g) means f(n)≤c·g(n), while Ω(g) means f(n)≥c·g(n).

Understand it
O and Ω are bounds. They are not automatically “worst” and “best” unless the function being bounded is specifically the worst-case or best-case running time.
Q4
Explain the difference between Big O, Big Theta, and Big Omega notations.
5 marks · CSE 21, Final · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
O(g(n)): upper bound; Ω(g(n)): lower bound; Θ(g(n)): tight two-sided bound.
Derivation / working
NotationMeaning for n ≥ n₀Interpretation
O(g)0 ≤ f(n) ≤ c·g(n)g grows at least as fast as f up to a constant
Ω(g)0 ≤ c·g(n) ≤ f(n)g grows no faster than f up to a constant
Θ(g)c₁g(n) ≤ f(n) ≤ c₂g(n)same asymptotic order

Example: 3n²+7n+4 is O(n²), Ω(n²), therefore Θ(n²).

Understand it
Θ is not “average case.” It means a tight asymptotic bound.
Q5
What is Asymptotic Complexity?
2 marks · CSE 16-17, Final; CSE 17, Final · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplementAsymptotic complexity describes how an algorithm's time or space grows as input size n becomes large, ignoring constant factors and lower-order terms. It is expressed with O, Ω and Θ.
Q6
What are the advantages and disadvantages of Asymptotic Analysis?
2 marks · 2017-18 Akash · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Advantages: machine-independent comparison and scalability analysis. Disadvantages: hides constant factors/cache/I-O effects and may not predict small-input running time.
Q7
What does it mean if an algorithm has a time complexity of O(n2 )?
1 mark · CSE 23, Final · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
O(n²) means that after some n₀ the running time is at most c·n² for some constant c.
Q8
Can we say the runtime of an algorithm is O(2n ) when the actual runtime is roughly (i) 2n/2 · C, (ii) 22n · C.
2 marks · CSE 19, Final · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
(i) C·2^(n/2) is O(2^n). (ii) C·2^(2n) is not O(2^n), because their ratio is 2^n and is unbounded.
Q9
What does it mean by θ(1) Complexity. What are the Time Complexities for building lps[] Array efficiently and Pattern Matching using KMP Algorithm?
2 marks · 2017-18 Akash · Bank p.3 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Θ(1) means constant growth. KMP builds LPS in Θ(m), searches text in Θ(n), total Θ(n+m).
Q10
What is the Best Case, Worst Case and Average Case Complexity of Quick Sort Algorithm?
2 marks · CSE 16-17, Final; CSE 17, Final · Bank p.3 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Quick Sort: best Θ(n log n), average Θ(n log n), worst Θ(n²).
Q11
Give an Example of Substitution Method.
2 marks · CSE 16-17, Final; CSE 17, Final · Bank p.3 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedExample: T(n)=T(n/2)+c, T(1)=1. Substitute repeatedly: T(n)=T(n/2²)+2c=…=T(n/2ᵏ)+kc. Base case n/2ᵏ=1 ⇒ k=log₂n. Thus T(n)=1+c log₂n=Θ(log n).
Q12
What is Recursion Tree Method?
2 marks · CSE 16-17, Final; CSE 17, Final · Bank p.3 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedDraw the recurrence as a tree: each node is one subproblem; label the non-recursive work at that node; compute number/size of nodes per level; find tree height from the base case; sum level costs. Example 2T(n/2)+n has n cost per level and log₂n levels, giving Θ(n log n).
Q13
“If f (n) = Θ(g(n)) then we can say f (n) = O(g(n)) but cannot say that f (n) = O(g(n))” — Justify the statement and explain your judgment.
1 mark · CSE 18, TT1 Set B · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
The wording is logically defective. If f=Θ(g), then f=O(g) and f=Ω(g). The converse f=O(g)⇒f=Θ(g) is not generally true.
Correction / source note
The first sentence in the PYQ repeats O(g(n)); answer uses the mathematically meaningful relation.
Q14
“We can say n! = O(n log n)” Justify the statement and explain your judgment.
1 mark · CSE 18, TT1 Set B · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
False. n! grows faster than n log n, so n!∉O(n log n).
Q15
Find gcd(A, C, n0 ), for the following f (n) such that f (n) ≤ C · g(n) where n > n0 , n0 > 0 and C > 0: f (n) = n + 7 + n2 + n3
2 marks · 2017-18 Akash · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Use the corresponding recurrence/asymptotic lesson: state the recurrence or counted operation, simplify it, and give the tightest asymptotic class supported by the derivation.
Q16
What do you mean by Optimal Solution?
2 marks · CSE 16-17, Final; CSE 17, Final · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
An optimal solution is a feasible solution with the best objective value among all feasible solutions.
Q17
Define Loop Invariant.
2 marks · CSE 19, Final · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
A loop invariant is a property true before the loop, preserved by every iteration, and used with termination to prove correctness.
Q18
Write the name of an algorithm which finds Single-Source Shortest Paths.
2 marks · CSE 20, Final · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Dijkstra’s algorithm is a single-source shortest-path algorithm for non-negative edge weights; Bellman–Ford is another SSSP method and supports negative edges.
Q19
Why do we need algorithms even if we have an unnatural computer having power to execute instantaneously and infinite memory?
2 marks · CSE 22, Final · Bank p.3 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Algorithms specify the correct finite procedure and prove correctness. Unlimited hardware cannot replace a precise method, nor make undecidable problems computable.
Q20
For a process to be called an algorithm, it must meet some conditions. Explain those conditions.
2 marks · CSE 23, Final · Bank p.4 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplementConditions: finiteness/termination; definiteness (unambiguous steps); zero or more clearly defined inputs; at least one output; effectiveness (each step is mechanically executable); correctness for valid inputs.
Q21
Write the names of two sorting algorithms that apply the Divide and Conquer approach. Write their time complexities in Best, Average, and Worst case scenarios.
2 marks · CSE 19, Final · Bank p.4 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Merge Sort: best/average/worst Θ(n log n). Quick Sort: best/average Θ(n log n), worst Θ(n²).
Q22
What is pitfall of Merge Sort over Quick Sort?
2 marks · CSE 20, Final · Bank p.4 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Merge Sort normally needs Θ(n) auxiliary array memory; in-place Quick Sort usually needs only recursion-stack space.
Q23
Time complexity is usually expressed as a function of input size. (CSE 23 TT1 MCQ: CPU brand / Input size / Programmer skill / Number of comments.)
2 marks · CSE 21, TT1 Set A; CSE 21, TT1 Set B · Bank p.4 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Correct option: Input size.
Q24
In time complexity analysis, small constant factors are usually ignored. (MCQ: Multiplied again / Ignored / Converted into variables / Written separately forever.)
2 marks · CSE 23, TT1 · Bank p.4 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Correct option: Ignored.
Q25
What does time complexity mainly measure? (MCQ: Amount of memory used by a program / Growth of running time with input size / Number of variables in a program / Size of the output.)
2 marks · CSE 23, TT1 · Bank p.4 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Correct option: Growth of running time with input size.
Q26
Which statements about O(1) space are true? (A) Extra memory does not grow with n; (B) Fixed number of variables may still be O(1); (C) It always means zero memory is used; (D) Constant extra space is considered efficient.
2 marks · CSE 23, TT1 · Bank p.4 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
A, B and D are true; C is false because O(1) space can still use a fixed positive amount of memory.
Q27
What is the Memory Complexity of the following function? Explain. void function1(n) { for(int i = n - 1; i >= 0; --i) func1(); }
2.5 marks · 2017-18 Akash · Bank p.4 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Assuming func1() itself is nonrecursive and O(1)-space, auxiliary space is O(1): calls happen sequentially rather than accumulating on the stack.
Q28
What is the Time Complexity of the following function, where n is a positive integer number? Explain. void function2(int n) { int i = 1, x = 0; while (x <= n) { i++; x = x + i; printf(""); if(i * 100 > n) break; } }
2.5 marks · 2017-18 Akash · Bank p.4 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Θ(√n). Since x increases roughly as 1+2+…+i=Θ(i²), x reaches n when i=Θ(√n).
Q29
What is the asymptotic running time of the following pseudocode? Write your answer using Θ notation. 1: procedure FOO(n) 2: for i <- 1 to n do 3: k <- i^5 4: while k > 1 do 5: k <- k/5 6: end while 7: end for 8: return i 9: end procedure
5 marks · CSE 20, Final · Bank p.5 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
The asymptotic running time is Θ(n log n).
Derivation / working

For fixed i, k starts at i⁵ and is divided by 5 until ≤1. Number of iterations is Θ(log₅(i⁵))=Θ(log i).

Total work = Σi=1..n Θ(log i) = Θ(log(n!)) = Θ(n log n) by Stirling’s approximation.

Q30
Which statements are true? for (int i = 1; i < n; i *= 2) { c++; } (a) Number of iterations is about log2 n (b) Complexity is O(log n) (c) Complexity is O(n) (d) c++ impacts the actual time calculation
2 marks · CSE 23, TT1 · Bank p.5 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
A and B are the intended true statements; tight complexity is Θ(log n). Formally, O(log n) is also O(n), so C is mathematically an upper bound but not the tight class.
Q31
What is the complexity of: for (int i = n; i > 0; i = sqrt(i)) { p++; } Options: O(log log n) / O(log n) / O(1) / O(n).
2 marks · CSE 23, TT1 · Bank p.5 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
As written with integer i, the loop becomes infinite at i=1 because sqrt(1)=1. If the intended condition were i>1, the intended bound is Θ(log log n).
Correction / source note
The PYQ code does not terminate as written.
Q32
Consider: for (int i = 0; i < n; i++) { for (int j = 0; j < 10; j++) { a++; } } Which are correct? (A) Total operations are 10n; (B) Time complexity is O(n); (C) Time complexity is O(n2 ); (D) Inner loop runs constant times.
2 marks · CSE 23, TT1 · Bank p.5 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
A, B and D are true. The inner loop runs exactly 10 times, so total work is 10n=Θ(n).
Q33
What is the auxiliary space complexity of: int* makeDouble(int arr[], int n) { int* out = new int[n]; for (int i = 0; i < n; i++) { out[i] = 2 * arr[i]; } return out; } Options: O(1) / O(log n) / O(n) / O(n2 ).
2 marks · CSE 23, TT1 · Bank p.5 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
O(n) auxiliary space because out is a newly allocated array of n integers.
Q34
Write a program of Complexity O(n + (log n)2 ).
2.5 marks · CSE 18, TT1 Set B · Bank p.6 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
One valid program is a Θ(n) loop plus two nested Θ(log n) loops.
Derivation / working
for i=0..n-1: work()
for i=1; i<n; i*=2:
  for j=1; j<n; j*=2: work()
Q35
Write a program of complexity n × 3.5.
2 marks · 2017-18 Akash · Bank p.6 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
n×3.5 is Θ(n); a loop executing floor(3.5n) constant-time operations has linear complexity.
Q36
Consider the idea of stack frames in function calls. Which statements are correct according to the topic? (A) The call stack follows LIFO order; (B) Each function call gets its own stack frame; (C) Number of total function calls is always equal to maximum simultaneous stack frames; (D) Recursive depth helps determine stack space.
2 marks · CSE 23, TT1 · Bank p.6 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
A, B and D are true. C is false: total function calls need not equal maximum simultaneously active stack frames.
Q37
Differentiate between the top-down and bottom-up approaches in dynamic programming. Using that approach which is less susceptible to stack overflow and compute factorial(4).
3 marks · CSE 23, Final · Bank p.6 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedTop-down = recursion + memoization; solve demanded states and cache them. Bottom-up = tabulation; iteratively fill states from base cases. Bottom-up is less susceptible to stack overflow because it avoids deep recursion. factorial(4): dp[0]=1; dp[1]=1; dp[2]=2; dp[3]=6; dp[4]=24.
Q38
Consider the following code snippet: Loop A: for(i = 1; i < 10000; i = i + 0.00001) { //constant time operation } Loop B: for(i = 1; i < n; i++) { //constant time operation } Loop C: for (i = 0; i < n; i++) { for (j = 0; j < i; j++) { if (j >= i/2) { for (k = 0; k < i * j; k++) { sum++; } } else { sum++; } } } All the loops are executed on the same machine and each iteration takes 1 microsecond. (a) Perform both a priori (theoretical) and a posteriori (empirical) analysis (considering n ≤ 100000) on Loop A and Loop B. Also evaluate the statement: “The actual execution time of an algorithm is equal to its time complexity.” (b) Analyze the time complexity of Loop C using the frequency count method.
5 marks · CSE 23, Final · Bank p.6 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
(a) Loop A is Θ(1) with respect to n but has about 9.999×10^8 iterations ≈1000 s at 1 μs each. Loop B is Θ(n), so for n=100000 ≈0.1 s. Actual time is not the same concept as asymptotic complexity. (b) Loop C is Θ(n⁴).
Derivation / working
For each i, Θ(i) values of j enter a k-loop of Θ(i·j)=Θ(i²), giving Θ(i³) for that i; Σi³=Θ(n⁴).
Q39
Consider the following code snippet: #include <iostream> #include <string> using namespace std; void justice(string &s, int start, int end, string &g1, string &g2, string &g3) { if (start > end) return; if (start == end) { if (start % 2 == 0) g1 += s[start]; else if (start % 2 == 1) g2 += s[start]; else g3 += s[start]; return; } int mid = (start + end) / 2; justice(s, start, mid, g1, g2, g3); if(mid == 6){ cout << "PLSTN WILL" << endl; } justice(s, mid + 1, end, g1, g2, g3); if(mid == 8){ cout << "BE FREE" << endl; } } int main() { string input = "HFIEOSLRRL-L-"; string g1 = "", g2 = "", g3 = ""; justice(input, 0, input.length() - 1, g1, g2, g3); cout << g1 << g2 << g3 << endl; return 0; } Demonstrate the use of the call stack in managing recursive function execution by illustrating both the winding (function call) and unwinding (return phase) for the recursive program. Determine the final output of the program based on the stack operations.
5 marks · CSE 23, Final · Bank p.6 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
The program prints the two conditional lines during unwinding and finally prints HIOLR--FESRLL.
Derivation / working
Winding (ranges):
justice(0,12)
├─ justice(0,6)
│  ├─ justice(0,3) ...
│  └─ justice(4,6) ...
└─ justice(7,12)
   ├─ justice(7,9) ...
   └─ justice(10,12) ...

Each base call appends one character to g1 or g2 according to index parity. On returns, the checks mid==6 and mid==8 print:

PLSTN WILL
BE FREE
HIOLR--FESRLL

The call stack follows LIFO: recursive calls wind until a base case, then return in reverse order.

Correction / source note
The third branch g3 is unreachable because every integer index is either even or odd; the final output follows the code exactly as written.
Q40
Find the Time Complexity of the recurrence relation T (n) = 2T (n/3) + n2.51 .
2 marks · 2017-18 Akash · Bank p.7 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Θ(n^2.51). n^2.51 dominates n^(log₃2), so Master case 3 applies.
Q41
For each of the following recurrences, give an expression for the runtime T (n) if the recurrence can be solved with the Master Theorem. Otherwise, indicate that the Master Theorem does not apply. (a) T (n) = 3T (n/2) + n2 (b) T (n) = T (n/2) + 2n (c) T (n) = 16T (n/2) + 1 (CSE 17 variant: 16T (n/2) + n) (d) T (n) = 2T (n/2) + n/ log(n)
8 marks · CSE 16-17, Final; CSE 17, Final · Bank p.7 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
(a) Θ(n²); (b) Θ(n); (c) Θ(n⁴) for both given variants; (d) Θ(n log log n) under the generalized Master form taught in the classroom notes.
Derivation / working
Parta,b,f(n)comparisonresult
a3,2,n²n^(log₂3)≈n^1.585; f polynomially largerΘ(n²)
b1,2,2nn^(log₂1)=1; f polynomially largerΘ(n)
c16,2,1 (or n)n^(log₂16)=n⁴ dominatesΘ(n⁴)
d2,2,n/log na=b^1 and p=−1Θ(n log log n)
Correction / source note
Many textbooks state only the three basic Master-Theorem cases; in that narrower form part (d) is “not directly applicable.” The supplied teacher notes explicitly include the p=−1 generalized case, so Θ(n log log n) is the teacher-aligned result.
Q42
Find the time complexity of T (n) = T (n/6) + T (5n/6) + c · n using Recurrence Tree Method.
3 marks · CSE 18, TT1 Set B · Bank p.8 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Θ(n log n). At each recursion-tree level the subproblem sizes sum to n, so work per level is Θ(n); height is Θ(log n).
Q43
What is the time complexity of the following Recurrence Relation, Explain: T (n) = 4T (n−1) if n > 0; T (n) = 100 otherwise.
2 marks · CSE 18, TT1 Set B · Bank p.8 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Θ(4^n): T(n)=4^nT(0)=100·4^n.
Q44
Find the Time Complexity of Merge Sort using Recurrence Tree Method.
5 marks · 2017-18 Akash · Bank p.8 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Merge Sort has Θ(n log n) running time.
Derivation / working

Recurrence: T(n)=2T(n/2)+cn.

Level 0: 1 node  × cn          = cn
Level 1: 2 nodes × c(n/2)      = cn
Level 2: 4 nodes × c(n/4)      = cn
...
Level log₂n−1:                  = cn
Leaves: n leaves × Θ(1)        = Θ(n)

Tree height is log₂n. Summing Θ(n) work across Θ(log n) levels gives Θ(n log n).

Q45
For the given recurrence T (n) = 2T (n/2) + cn and T (1) = 1, draw the recurrence tree for n = 16 and find out the complexity of the recurrence in big O notation.
5 marks · CSE 21, Final · Bank p.8 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
For n=16, the recurrence tree gives Θ(n log n) (hence O(n log n)).
Derivation / working
T(16)
├─ 2T(8)  + 16c        level cost = 16c
├─ 4T(4)  + 16c        level cost = 16c
├─ 8T(2)  + 16c        level cost = 16c
└─ 16T(1) + 16c        leaf cost  = 16

There are log₂16=4 internal levels. Total = 4·16c + 16 = Θ(16 log₂16). General form: Θ(n log n).

Q46
Let the data 63, 71, 82, 64, 56, 40, 55, 74 are stored in A[1] to A[8]. (a) Now show each step to sort those data using Merge Sort Algorithm. [8] (b) What will be the best, average and worst case for MergeSort technique while sorting N numbers? [2]
10 marks · CSE 16-17, Final; CSE 17, Final · Bank p.8 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Final sorted order: 40,55,56,63,64,71,74,82. Merge Sort best = average = worst = Θ(n log n).
Derivation / working
Split:
[63,71,82,64,56,40,55,74]
→ [63,71,82,64] | [56,40,55,74]
→ [63,71] [82,64] | [56,40] [55,74]
→ [63][71][82][64][56][40][55][74]

Merge:
[63]+[71] → [63,71]
[82]+[64] → [64,82]
[56]+[40] → [40,56]
[55]+[74] → [55,74]
[63,71]+[64,82] → [63,64,71,82]
[40,56]+[55,74] → [40,55,56,74]
final → [40,55,56,63,64,71,74,82]

There are log₂n merge levels and Θ(n) work per level, so Θ(n log n) regardless of input order.

Q47
Apply Merge-sort algorithm to sort the following array. arr[] = {2, 2, 3, 4, 13, 8, 9, 10, 5}. Just show the states as the arrays divide and merge at each step. You don’t have to indicate how it is happening.
5 marks · CSE 19, Final · Bank p.8 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Sorted array: [2,2,3,4,5,8,9,10,13].
Derivation / working
Divide:
[2,2,3,4,13,8,9,10,5]
→ [2,2,3,4] | [13,8,9,10,5]
→ [2,2] | [3,4] | [13,8] | [9,10,5]
→ [2]|[2]|[3]|[4]|[13]|[8]|[9]|[10]|[5]

Merge:
[2]+[2]→[2,2]
[3]+[4]→[3,4]
[13]+[8]→[8,13]
[10]+[5]→[5,10]
[9]+[5,10]→[5,9,10]
[2,2]+[3,4]→[2,2,3,4]
[8,13]+[5,9,10]→[5,8,9,10,13]
final→[2,2,3,4,5,8,9,10,13]
Q48
Apply the Quick Sort algorithm to the array [4, 7, 2, 9, 1, 4] to sort it in descending order. Show all intermediate steps, including partitioning and recursive calls, until the final sorted array is obtained.
3 marks · CSE 23, Final · Bank p.8 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
A valid descending Quick Sort result is [9,7,4,4,2,1].
Derivation / working
Use the class-note first-element-pivot partition; exact intermediate arrays may vary with the implementation, but every partition must put values ≥ pivot to its left and values ≤ pivot to its right.
Q49
Demonstrate how divide and conquer technique is used in this algorithm to find the key ele- ment k = −7 for the following entries: −15, −5, 0, 9, 14, 23, 46, 54, 63, 113, 123, 141, 144, 151. (CSE 17 variant has 83 in place of 63.)
10 marks · CSE 16-17, Final; CSE 17, Final · Bank p.8 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Using binary search (divide and conquer), −7 is not present.
Derivation / working
Steplow..highmiddle valueDecision
1−15 … 15146−7<46 ⇒ left half
2−15 … 230−7<0 ⇒ left half
3−15 … −5−15−7>−15 ⇒ right half
4−5 … −5−5−7<−5 ⇒ left ⇒ empty

Each comparison halves the search interval, so time is O(log n).

Q50
You want to invest some money in buying share of a company X and selling it at the right time to bag the maximum profit. The price per share is as follows on different days: Days 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Price (BDT) 12 10 13 12 12 13 9 14 12 15 16 14 19 21 17 (a) Find the day of buying and the day of selling to ensure the maximum profit using the divide and conquer approach. [6] (b) Why do you think the divide and conquer approach optimized the time complexity than the naive/brute-force approach for this problem? [1] (c) Can you find a better way to optimize the solution than the divide and conquer approach? [3]
10 marks · CSE 22, Final · Bank p.8 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Buy day 1 at 10 BDT and sell day 13 at 21 BDT; maximum profit = 11 BDT.
Derivation / working

(a) Divide-and-conquer: split days into left/right halves. For each segment return the best internal trade and the best crossing trade (minimum price in left, maximum price in right). Combining recursively finds the same 11-BDT optimum.

(b) Naive checks all buy/sell pairs: Θ(n²). A straightforward divide-and-conquer combine can be Θ(n log n).

(c) Better: one pass. Maintain minimum price seen so far and update best=max(best, price[i]-minPrice). Time Θ(n), space Θ(1).

Q51
Which sorting algorithm first divides the array into smaller halves and then combines them back in sorted order? (MCQ: Bubble Sort / Merge Sort / Quick Sort / Selection Sort.)
2 marks · CSE 23, TT1 · Bank p.8 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Merge Sort.
Q52
After partitioning in Quick Sort, which are true? (A) Elements less than or equal to pivot are on the left; (B) Elements greater than pivot are on the right; (C) Pivot stays in its correct sorted position; (D) Entire array becomes sorted immediately.
2 marks · CSE 23, TT1 · Bank p.9 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
A, B and C are true; D is false. One partition places the pivot correctly but does not fully sort both sides.
Q53
Which are true about the base case in recursive sorting? (A) It stops further recursive calls; (B) A subarray of size 0 or 1 is already sorted; (C) It is necessary in both Merge Sort and Quick Sort; (D) It always means the whole original array is sorted in one step.
2 marks · CSE 23, TT1 · Bank p.9 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
A, B and C are true; D is false.
Q54
Which data structure is suitable to implement a Priority Queue? What are the complexities of push(x), pop(), and top() operations?
2 marks · CSE 19, Final · Bank p.9 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Binary heap: push O(log n), pop O(log n), top/peek O(1).
Q55
Design a special stack such that maximum element can be found in O(1).
2 marks · 2017-18 Akash · Bank p.9 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Store (value,currentMax) at every stack node, or maintain a second max-stack. Then push, pop and getMax are all O(1).
Q56
Insert the following numbers sequentially in a Min-Heap. 15, 10, 12, 20, 12, 5 Then POP the smallest element from the Min-Heap.
6 marks · CSE 19, TT2 · Bank p.9 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
After all inserts the min-heap is [5,12,10,20,15,12]. After one POP it becomes [10,12,12,20,15].
Derivation / working
OperationHeap array
insert 15[15]
insert 10[10,15]
insert 12[10,15,12]
insert 20[10,15,12,20]
insert 12[10,12,12,20,15]
insert 5[5,12,10,20,15,12]
pop 5[10,12,12,20,15]

Insertion bubbles the new item upward; pop moves the last item to the root and heapifies downward. Each is O(log n).

Complete previous-year question map

Hash Tables

All bank questions for this topic are represented. Question stays visible; answer stays collapsed. Clear duplicates are consolidated while every occurrence and alternate wording is retained.

PYQ11 bank questions
Q1
What is hashing?
1 mark · CSE 23, Final · Bank p.9 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplementHashing uses a hash function h(key) to map a key to an index/bucket in a hash table so lookup, insertion and deletion can be fast on average.
Q2
Write at least Three Purpose of Hashing.
2 marks · CSE 16-17, Final; CSE 17, Final · Bank p.9 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Purposes include fast dictionary lookup, set membership/duplicate detection, indexing/caching, and string matching.
Q3
What is Geometric Hashing?
2 marks · CSE 16-17, Final; CSE 17, Final · Bank p.9 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Geometric hashing maps geometric features/transform-invariant descriptions to hash keys so similar shapes can be retrieved efficiently.
Q4
What are the four basic properties to be a good hash function?
2 marks · CSE 19, Final · Bank p.9 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplementA good table hash function should be deterministic, fast to compute, distribute expected keys approximately uniformly, and depend on the whole key so regular input patterns do not create many collisions.
Q5
Explain the significance of a good hash function and its properties.
5 marks · CSE 21, Final · Bank p.9 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
A good hash function maps keys quickly and spreads them almost uniformly, keeping collisions and clustering low so hash-table operations stay near expected O(1).
Derivation / working
  • Deterministic: same key → same index.
  • Uniform: distributes typical keys across all slots.
  • Fast: O(1) or linear in key length.
  • Uses the whole key / avalanche-like behavior: small key changes should change the hash well.

Bad distribution creates long probe/chain lengths and can degrade operations toward O(n).

Q6
What does it mean while saying “The hash function has collision resistance property”?
1 mark · CSE 23, Final · Bank p.9 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Collision resistance means distinct keys should rarely/effectively not be easy to force to the same hash value; ordinary hash tables still require collision handling.
Q7
Why it is a bad idea to map the 0 to indicate a character during Hashing?
2 marks · CSE 19, TT2 · Bank p.9 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Mapping a character to 0 can make leading symbols contribute nothing in a polynomial hash and creates unnecessary collisions; use positive codes.
Q8
How does the usage of hashing in the Rabin-Karp algorithm for string matching improve the performance of the Naive String Matching technique?
5 marks · CSE 21, Final · Bank p.9 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Rabin–Karp improves naive matching by comparing a rolling hash of each length-m text window with the pattern hash before doing character-by-character verification.
Derivation / working

Initial pattern/window hashes cost O(m). Moving one position updates the window hash in O(1): remove the outgoing character, shift/base-multiply, add the incoming character, then reduce modulo q. Only when hashes match is a direct comparison required.

Expected/average time is O(n+m) with a good hash; worst case remains O(nm) if many spurious hits occur.

Understand it
A hash hit is only a candidate match because collisions are possible; verify the substring.
Q9
Explain how to implement a hash table using open addressing with linear probing.
5 marks · CSE 21, Final · Bank p.9 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplementAllocate table size m. For key k, probe h(k,i)=(h(k)+i) mod m for i=0,1,… until an EMPTY/DELETED slot is found; insert there. Search follows the same sequence and stops at an EMPTY slot or the key. Deletion uses a tombstone rather than ordinary EMPTY so later probes remain reachable. Expected operations are O(1) at controlled load; worst O(m).
Q10
Calculate the Hash value of the word aacaabcb with the mapping 1 → a, 2 → b, 3 → c. Use the prime 17 as the base and mod the values at each step with 107.
5 marks · CSE 19, Final · Bank p.9 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Final hash = 9.
Derivation / working

Use iterative polynomial hashing h=(h·17+value) mod 107, with a=1,b=2,c=3.

Prefixhash
a1
aa18
aac95
aaca11
aacaa81
aacaab95
aacaabc13
aacaabcb9
Q11
Calculate the hash value of the word aacaabacb with the mapping 1 → a, 2 → b, 3 → c. b = 13, mod = 137.
4 marks · CSE 22, Final · Bank p.9 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Using h=(h·13+value) mod 137 with a=1,b=2,c=3 gives states 1,14,48,77,43,13,33,21,1; final hash=1.
Complete previous-year question map

Greedy Algorithms

All bank questions for this topic are represented. Question stays visible; answer stays collapsed. Clear duplicates are consolidated while every occurrence and alternate wording is retained.

PYQ19 bank questions
Q1
What is the greedy choice for a greedy algorithm?
1 mark · CSE 21, Final · Bank p.10 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplementAt each step choose the locally best feasible option according to the problem's greedy criterion, without revisiting earlier choices.
Q2
Give an example of a problem that can be solved using a greedy algorithm.
1 mark · CSE 23, Final · Bank p.10 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Example: fractional knapsack, Dijkstra (non-negative edges), Prim, Kruskal, activity selection.
Q3
What are the key characteristics of a Greedy approach in solving optimization problems?
1 mark · CSE 23, Final · Bank p.10 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Greedy builds incrementally, makes a locally optimal feasible choice, and is correct only when the greedy-choice property holds; optimal substructure is also typical.
Q4
Which key property is shared by both greedy algorithms and dynamic programming?
2 marks · CSE 20, Final · Bank p.10 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedOptimal substructure: an optimal solution contains optimal solutions to appropriate subproblems. Greedy additionally needs a greedy-choice property; DP typically also exploits overlapping subproblems.
Q5
Discuss any scenarios where the Greedy approach might not yield an optimal solution.
5 marks · CSE 21, Final · Bank p.10 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Greedy can fail when the locally best choice destroys a better global combination; therefore a greedy algorithm needs a proof such as an exchange argument/greedy-choice property.
Derivation / working

Example: 0/1 knapsack. Capacity 50; items (value,weight): (60,10),(100,20),(120,30). Ratio-greedy takes first two for value 160, but the optimum is items 2+3 for value 220. Fractional knapsack is different because fractions allow the ratio choice to remain optimal.

Q6
Determine whether the following statement is true or false and explain your answer: “The Floyd-Warshall all-pairs shortest path algorithm for finding the shortest distances between nodes in a graph is an example of a Greedy Algorithm”.
2 marks · 2017-18 Akash · Bank p.10 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
False. Floyd–Warshall is dynamic programming, not greedy.
Q7
Why do you think 0/1 Knapsack is a dynamic programming problem whereas the fractional Knapsack is a greedy problem?
1 mark · CSE 22, Final · Bank p.10 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedFractional knapsack allows splitting items, so taking maximum profit/weight first is exchange-safe and greedy is optimal. 0/1 knapsack cannot split items; a locally best item can block a better combination, so DP compares include/exclude states.
Q8
What would be the time complexity of Fractional Knapsack Problem using Greedy Algorithm?
1 mark · CSE 18, TT1 Set B · Bank p.10 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedCompute ratios and sort n items by profit/weight: O(n log n); one scan is O(n). Total O(n log n). If already sorted by ratio, the selection scan is O(n).
Q9
There are different selection strategies to find compatible jobs in the job scheduling algorithm. Briefly discuss the challenges with examples for the following selection strategy: earliest start time or earliest finish time or shortest interval.
5 marks · CSE 21, Final · Bank p.10 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
For maximizing the number of mutually compatible activities, earliest finish time is the safe greedy rule. Earliest start and shortest duration are not generally safe.
Derivation / working

Earliest start failure: an activity starting first may run very long and block many short later activities.

Shortest duration failure: the shortest activity may lie in the middle of the timeline and block one activity before and one after; choosing a slightly longer earlier-finishing activity can allow more total activities.

Earliest finish proof idea: replacing the first activity of any optimal solution with the earliest-finishing compatible activity leaves at least as much room for the remaining activities.

Q10
Find the Maximum Profit and Order of Scheduled Jobs for Job Scheduling Problem from the following jobs using Greedy Algorithm. Job(A, 2, 1), Job(B, 3, 30), Job(C, 1, 10), Job(D, 5, 50), Job(E, 4, 40) Here jobs are given as Job(ID, Deadline, Profit) format.
5 marks · 2017-18 Akash · Bank p.10 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Maximum profit = 131; one valid schedule is [C, A, B, E, D] in slots 1…5.
Derivation / working
Jobdeadlineprofitlatest free slot
D5505
E4404
B3303
C1101
A212

Sort jobs by descending profit and place each in the latest free slot ≤ deadline. Total = 10+1+30+40+50=131.

Q11
Find the Maximum Profit and Order of Scheduled Jobs for Job Scheduling Problem from the following jobs using Greedy Algorithm. Job1(2, 50), Job2(1, 40), Job3(1, 55), Job4(3, 60), Job5(3, 55). Here jobs are given as JobID(Deadline, Profit) format.
4 marks · CSE 18, TT1 Set B · Bank p.10 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Maximum profit=170; one schedule is ['J3', 'J5', 'J4'].
Q12
Assume there is only one classroom and you have to schedule the following classes. The duration of each class is 1 hour and your objective is to maximize the total number of students served. • Classes start from 11:00 am in the morning. • Each of the classes must end before their respective deadline. • Classes can be arranged in any arbitrary order. It is okay to start CSE333 anytime from 11:00 am to 1:00 pm, as long as it ends before 2:00 pm. Class Code Deadline Students CSE133 2:00 pm 130 CSE233 12:00 pm 75 CSE127 1:00 pm 100 CSE337 2:00 pm 110 CSE333 2:00 pm 120
5 marks · CSE 19, Final · Bank p.10 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Maximum students served = 360. One optimal schedule: CSE337 11–12, CSE333 12–1, CSE133 1–2.
Derivation / working

Convert deadlines to slots from 11:00: deadline 12→slot1, 1→slot2, 2→slot3. Treat number of students as profit and use job-sequencing-by-profit.

Classdeadlinestudentsplaced
CSE1332 pm130slot 3
CSE3332 pm120slot 2
CSE3372 pm110slot 1
CSE1271 pm100no free slot ≤2
CSE23312 pm75no free slot 1

Total = 130+120+110 = 360.

Correction / source note
Correction: an earlier transcription incorrectly used 305 students.
Q13
Apply the greedy activity selection algorithm to the following set of activities. Activity A B C D E F G H I Start Time 1 1 1 2 3 4 5 6 7 Finish Time 2 3 4 5 7 9 6 8 9 (a) Show all steps to find the selected activities. [7] (b) What is the running time of this algorithm? [1] (c) Have you found the optimal solution? If yes, state why; if no, propose another way to find the optimal solution. [2]
10 marks · CSE 16-17, Final; CSE 17, Final · Bank p.11 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Selected activities: A → D → G → H; maximum count = 4. Running time is O(n log n) including sorting, or O(n) if already sorted by finish time.
Derivation / working
Activitystartfinishdecision
A12select
B13reject: start<2
C14reject
D25select
G56select
E37reject
H68select
F49reject
I79reject: start<8

Earliest-finish-time greedy is optimal by the exchange argument: replacing the first activity of any optimal solution with the earliest-finishing compatible activity cannot reduce the remaining scheduling room.

Q14
Solve the Activity Selection Problem using Greedy Method for the following activities: {(4, 8), (3, 10), (1, 3), (2, 6), (4, 6), (3, 4), (7, 9)}.
3 marks · 2017-18 Akash · Bank p.11 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Sort activities by increasing finish time; select the first, then repeatedly select the next activity whose start is at least the last selected finish. This rule is optimal for maximizing the number of compatible activities; sorting costs O(n log n), scan O(n).
Q15
Given a set of activities A with start and finish times, find the maximum number of activities that can be performed without overlapping: A = {(1, 1, 4), (2, 3, 5), (3, 0, 6), (4, 5, 7), (5, 3, 9), (6, 5, 9), (7, 6, 10), (8, 7, 11), (9, 8, 12), (10, 2, 14), (11, 12, 16)}.
10 marks · CSE 21, Final · Bank p.11 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Maximum number of non-overlapping activities = 4. One optimal set is {1,4,8,11}.
Derivation / working
ID(start,finish)decision
1(1,4)select
2(3,5)reject
3(0,6)reject
4(5,7)select
5(3,9)reject
6(5,9)reject
7(6,10)reject
8(7,11)select
9(8,12)reject after 8
10(2,14)reject
11(12,16)select

Sort by finish time, then repeatedly choose the next activity whose start ≥ finish of the last selected activity.

Q16
Determine the change that would incur the minimum number of coins for an amount of 16 cents (initial coin denomination values used in the US, i.e. 25, 10, 5, 1 cents).
5 marks · CSE 20, Final · Bank p.11 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
For 16 cents with {25,10,5,1}, minimum number of coins is 3: 10+5+1.
Derivation / working

Greedy picks the largest denomination not exceeding the remaining amount: 16→take10 (remain6)→take5 (remain1)→take1.

For standard US denominations this greedy rule is canonical and gives the optimum here.

Q17
Find the Optimal Binary Codeword using Huffman Coding algorithm using the following information. Is this coding beneficial? If yes, justify your answer. Character Frequency a 15 b 11 c 36 d 42 e 55 f 9
10 marks · CSE 20, Final · Bank p.11 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
One optimal Huffman code is a=000, b=0011, c=01, d=10, e=11, f=0010 (0/1 sides may be swapped and still be optimal).
Derivation / working

Frequencies: a15,b11,c36,d42,e55,f9. Merge smallest repeatedly: 9+11=20; 15+20=35; 35+36=71; 42+55=97; 71+97=168.

charfreqcodebits contributed
a1500045
b11001144
c360172
d421084
e5511110
f9001036

Total Huffman bits = 391 for 168 symbols. Fixed-length coding for 6 symbols needs 3 bits/symbol = 504 bits. Saving = 113 bits (~22.4%), so it is beneficial.

Q18
For the given frequencies, a = 40, b = 31, c = 3, d = 6, e = 12, f = 16, g = 1 generate the Huffman tree and write down the prefix codes for a, b, c, d, e, f and g.
5 marks · CSE 21, Final · Bank p.11 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
One optimal prefix code is a=0, b=10, c=111001, d=11101, e=1111, f=110, g=111000.
Derivation / working

Merge sequence: 1+3=4; 4+6=10; 10+12=22; 16+22=38; 31+38=69; 40+69=109. Assign 0/1 down the final tree. Any left/right swap gives an equivalent optimal code.

Q19
There are n groups of people trying to schedule meetings at the same time, and there are m meeting rooms available. Each group i (i = 1, 2, . . . , n) has ai people, and each room j (j = 1, 2, . . . , m) has a capacity of bj . Group i can be scheduled to use room j if and only if ai ≤ bj . Of these n groups, the first k (1 ≤ k ≤ m) groups have high priority and they need to be scheduled (there will always be at least one way to schedule them). Design an algorithm that schedules all the high priority groups, and schedules as many other groups as possible.
10 marks · CSE 20, Final · Bank p.12 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Use a sorted multiset (balanced BST) of room capacities. First reserve rooms for all k high-priority groups, then greedily place as many remaining groups as possible into the smallest room that can hold each group.
Derivation / working

High-priority phase: process the k required groups in descending group size; for each size aᵢ, choose lower_bound(aᵢ) — the smallest feasible room — and remove it. Descending order avoids wasting large rooms needed by larger required groups.

Optional phase: sort remaining groups by size ascending and again assign each to the smallest remaining feasible room. This maximizes cardinality because no group consumes more capacity than necessary.

Sorting costs O((n+m)log(n+m)); each lookup/removal is O(log m). Overall O((n+m)log(n+m)).

Complete previous-year question map

Dynamic Programming

All bank questions for this topic are represented. Question stays visible; answer stays collapsed. Clear duplicates are consolidated while every occurrence and alternate wording is retained.

PYQ41 bank questions
Q1
Which problem does DP address?
1 mark · CSE 21, Final · Bank p.12 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplementDP is suitable when a problem has optimal substructure and repeated/overlapping subproblems; store subproblem results so each state is solved once.
Q2
In which situations can DP be used?
1 mark · CSE 23, Final · Bank p.12 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Use DP when the same subproblems repeat and an optimal solution can be built from optimal subsolutions.
Q3
Define the concept of overlapping subproblems in dynamic programming.
1 mark · CSE 21, Final · Bank p.12 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedOverlapping subproblems means the same smaller states are reached repeatedly by a naive recursive solution. DP stores each state's answer and reuses it instead of recomputing it.
Q4
Define the “overlapping subproblems” property of DP algorithm to solve the Fibonacci problem.
2 marks · CSE 20, Final · Bank p.12 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Recursive Fibonacci recomputes fib(k) in many branches; memoization stores each fib(k) once.
Q5
If a problem does not have overlapping subproblems, what would happen if Dynamic Programming is used to solve the problem?
2 marks · 2017-18 Akash; CSE 19, Final; CSE 22, Final · Bank p.12 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Without overlapping subproblems, memoization/table storage gives little benefit and may only add overhead.
Q6
For a dynamic programming algorithm, computing all values in a bottom-up fashion is asymptotically faster than using recursion and memoization. Is it true? Justify your answer.
2 marks · CSE 19, Final · Bank p.12 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
False in general. Bottom-up and memoized top-down usually have the same asymptotic state/transition count; bottom-up may have lower overhead while top-down may skip unreachable states.
Q7
List basic differences between the memoization and tabulation approach used in DP solutions.
2 marks · CSE 20, Final · Bank p.12 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplementMemoization: top-down recursion, compute only reached states, cache results; recursion stack is used. Tabulation: bottom-up iteration from base states, usually computes a planned table and avoids recursion. Both remove repeated subproblem work.
Q8
Sometimes Bottom-up approach of dynamic programming can be more memory-efficient compared to the Top-down approach. True or False? If False, write the correct statement.
2 marks · CSE 19, Final · Bank p.12 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
True sometimes: bottom-up can reuse only recent rows and avoid recursion-stack memory, but it is not universally more memory-efficient.
Q9
List any four famous algorithms to solve problems in DP paradigm.
2 marks · CSE 20, Final · Bank p.12 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Examples: 0/1 knapsack, LCS, coin change, Matrix Chain Multiplication.
Q10
Normally for a DP Problem the complexity is the number of states. But for Matrix Chain Multiplication it is not true, what is the reason?
1 mark · CSE 18, TT1 Set B; 2017-18 Akash · Bank p.12 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
MCM has O(n²) interval states but O(n) split choices per state, so time is O(n³).
Q11
Let’s assume we are applying Bottom Up DP to solve 0-1 Knapsack Problem where the number of items are 5 and max capacity is 10, then what does it mean by cell[3, 7]/dp[3][7] of the table?
1 mark · CSE 18, TT1 Set B · Bank p.12 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
dp[3][7] means the maximum value attainable with capacity 7 using only the first 3 items.
Q12
Memory complexity of the Matrix-Chain-Multiplication algorithm is O(N 2 ). True or False? If False, write the correct statement.
2 marks · CSE 19, Final · Bank p.12 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
True for standard MCM: m and split tables require O(n²) memory.
Q13
Suppose that the matrix Ai has dimensions pi−1 × pi . What is the dimension of the matrix product U = Ai Ai+1 Ai+2 . . . An ?
2 marks · CSE 20, Final · Bank p.12 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
AiAi+1…An has dimension p(i−1) × pn.
Q14
What is the purpose of LCS?
2 marks · CSE 20, Final · Bank p.13 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedLCS finds the longest sequence of symbols appearing in the same relative order in two sequences, not necessarily contiguously. Uses include similarity/diff, DNA comparison and sequence alignment.
Q15
What is the formula to calculate LCS value in a cell using Bottom Up DP?
2 marks · 2017-18 Akash · Bank p.13 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedFor strings X,Y: if X[i−1]=Y[j−1], L[i][j]=1+L[i−1][j−1]; otherwise L[i][j]=max(L[i−1][j],L[i][j−1]). Base row/column = 0.
Q16
What is meant by the longest common subsequence between two strings? Give an example.
1 mark · CSE 22, Final · Bank p.13 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
A longest common subsequence is the longest sequence that is a subsequence of both strings. Example: LCS of ABCD and AEBD can be ABD.
Q17
What do you understand by increasing subsequence? Give an example.
1 mark · CSE 22, Final · Bank p.13 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
An increasing subsequence keeps original order and every next value is larger; e.g. 3,8,9 from 3,4,13,8,9.
Q18
Find LCS length of “PQRQR” and “QRPQRN” using Dynamic Programming.
3 marks · CSE 18, TT1 Set B · Bank p.13 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedThe LCS length is 4; one LCS is QRQR. Fill a (|X|+1)×(|Y|+1) table with the standard LCS recurrence; the bottom-right value is 4, then backtrack diagonally on equal characters to recover QRQR.
Q19
Calculate the Longest Increasing Sub-sequence of the following array. You can use either Top-down or Bottom-up approaches. Show each steps/table. arr[] = {3, 4, 13, 8, 9}.
5 marks · CSE 19, Final · Bank p.13 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
LIS length = 4; one LIS is [3,4,8,9].
Derivation / working
ivaluebest LIS ending herelength
0331
143,42
2133,4,133
383,4,83
493,4,8,94

DP recurrence: L[i]=1+max(L[j]) for j<i and A[j]<A[i], else 1. Answer=max L[i]. O(n²) time for this table method.

Q20
(a) What do you understand by increasing subsequence? Give an example. [1] (b) You’re given the following numbers: A = {−2, −3, 0, −2, 1, 2, −1, 0, 1, 2, 5, 3, 4, 6, −2, 7, 0}. Find the length of longest increasing subsequence (LIS) in the given number list using the bottom-up approach. [5] (c) Which numbers make up the LIS? [Show using path printing] [2] (d) How can you modify the algorithm to find the longest non-decreasing subsequence instead of the longest increasing subsequence? [2]
10 marks · CSE 22, Final · Bank p.13 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
(a) An increasing subsequence preserves index order and has strictly increasing values. Example: 1,3,5. (b) LIS length = 10. (c) One LIS is −3,−2,−1,0,1,2,3,4,6,7. (d) For non-decreasing subsequence change the comparison from A[j] < A[i] to A[j] ≤ A[i].
Derivation / working
iA[i]L[i]
0-21
1-31
202
3-22
413
524
6-13
704
815
926
1057
1137
1248
1369
14-22
15710
1604

Store parent[i] whenever L[i] improves; follow parents backward from the index with L=10 to print the LIS.

Q21
(a) What is meant by the longest common subsequence between two strings? Give an example. [1] (b) Consider the following two strings: S1 = “CHOCO”, S2 = “COFFEE”. You have to convert the S1 string to S2 string. You can perform only 3 operations. You can delete any character on cost 1, add any character on cost 2 or modify any character on cost 3. You have to minimize the cost to perform this conversion using top-down approach. [6] (c) Show from b, which operations you performed to make this conversion. [Show using path printing] [3]
10 marks · CSE 22, Final · Bank p.13 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
(a) LCS is the longest sequence appearing in both strings in the same relative order, not necessarily contiguously. (b) Minimum conversion cost CHOCO→COFFEE = 11. (c) One optimal operation path is shown below.
Derivation / working
CHOCO
-delete H (cost1) → COCO
-insert F,F,E,E after O (cost8) → COFFEECO
-delete C,O (cost2) → COFFEE
Total = 1+8+2 = 11

Top-down state F(i,j) = minimum cost to convert suffix S1[i:] to S2[j:]. Match → F(i+1,j+1); otherwise min(delete:1+F(i+1,j), insert:2+F(i,j+1), replace:3+F(i+1,j+1)). Memoization gives O(|S1||S2|).

Q22
You’re given two strings as s1 = "XBTAYPOZARBC" and s2 = "AXATYABCD". (a) Find the longest common subsequence of these strings using iterative approach. (b) Find the LCS from the table.
10 marks · CSE 22, TT2 · Bank p.13 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
LCS length = 6; one LCS recovered from the table is XTYABC.
Derivation / working

Iterative recurrence: if characters match, dp[i][j]=dp[i−1][j−1]+1; otherwise take max(dp[i−1][j],dp[i][j−1]).

∅AXATYABCD
∅0000000000
X0011111111
B0011111222
T0011222222
A0112223333
Y0112233333
P0112233333
O0112233333
Z0112233333
A0112234444
R0112234444
B0112234555
C0112234566

Backtrack from bottom-right: move diagonally and record the character on a match; otherwise move to the neighbor with the larger value. Reversing the recorded characters gives XTYABC.

Q23
Given S1 = “SPARE” and S2 = “STARES”, find the edit distance of them using dynamic programming, and show the cost matrix.
10 marks · CSE 21, Final · Bank p.13 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Levenshtein edit distance between SPARE and STARES = 2.
Derivation / working

Initialize first row/column as 0…m and 0…n. Match → copy diagonal; mismatch → 1+min(delete, insert, replace).

∅STARES
∅0123456
S1012345
P2112345
A3221234
R4332123
E5443212

The bottom-right cell is 2.

Q24
Find the minimum edit distance between ‘INTENTION’ and ‘EXECUTION’. Show each step to describe your algorithm.
10 marks · CSE 16-17, Final; CSE 17, Final · Bank p.13 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Minimum edit distance = 5 (unit insertion/deletion/substitution costs).
Derivation / working

Use the same recurrence as Q23. The completed matrix is:

∅EXECUTION
∅0123456789
I1123456678
N2223456777
T3333455678
E4343456678
N5444456777
T6555555678
I7666666567
O8777777656
N9888888765

Bottom-right = 5. One optimal transformation can be obtained by backtracking through equal/minimum predecessor cells.

Q25
The recursive formula of Longest Common Subsequence problem is:  0 if i = 0 or j = 0   c[i][j] = c[i − 1][j − 1] + 1 if i, j > 0 and xi = yj  max(c[i][j − 1], c[i − 1][j]) if i, j > 0 and x ̸= y  i j An input sequence of two strings generates a table for value c given below. Now, reconstruct the strings which produce the c table. i↓j→ 0 1 2 3 4 5 6 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 2 0 1 1 1 1 1 2 3 0 1 1 2 2 2 2 4 0 1 2 2 2 3 3 5 0 1 2 2 3 3 3 6 0 1 2 2 3 3 4 7 0 1 2 2 3 3 4
10 marks · CSE 20, Final · Bank p.14 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
The numeric c table determines the LCS lengths, but it does not uniquely determine the original two strings. Therefore the strings cannot be reconstructed uniquely from c alone.
Derivation / working

The recurrence increases diagonally when xᵢ=yⱼ, but the table stores only numbers, not the matched symbol. Many different character assignments can produce the same pattern of increases. To reconstruct actual strings, the characters X,Y (or equivalent character/path information) must also be supplied.

Correction / source note
This PYQ is under-specified as reproduced in the supplied bank. A unique pair of input strings cannot be inferred from the c table alone.
Q26
Write the pseudocode to find all possible LCSs with length K for given (calculated) LCS tables — c (cost) and b (path) of two strings X = BDABAG, Y = ABACBDAB. Apply your algorithm and find the answer(s).
10 marks · CSE 21, Final · Bank p.14 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
For X=BDABAG and Y=ABACBDAB, LCS length is 4. The distinct LCSs are BDAB and BABA.
Derivation / working
AllLCS(i,j):
  if i==0 or j==0: return {""}
  if X[i-1]==Y[j-1]:
      return { s + X[i-1] : s in AllLCS(i-1,j-1) }
  A = AllLCS(i-1,j) if c[i-1][j] >= c[i][j-1] else {}
  B = AllLCS(i,j-1) if c[i][j-1] >= c[i-1][j] else {}
  return A ∪ B

Memoize each (i,j) and deduplicate strings. Keep only results whose length equals K=4.

Q27
You are given the cost matrix b[] for two strings “ALGORITHM” and “ALIGNMENT”. How do you modify the LCS DP SOLUTION algorithm to find the ALL LCSs having the highest length. For example, for the given b[], the output will be “2 LCSs”; one is ALGM and the other is ALGT. Xi \Yj ε A L G O R I T H M ε 0 0 0 0 0 0 0 0 0 0 A 0 1 1 1 1 1 1 1 1 1 L 0 1 2 2 2 2 2 2 2 2 I 0 1 2 2 2 2 3 3 3 3 G 0 1 2 3 3 3 3 3 3 3 N 0 1 2 3 3 3 3 3 3 3 M 0 1 2 3 3 3 3 3 3 4 E 0 1 2 3 3 3 3 3 3 4 N 0 1 2 3 3 3 3 3 3 4 T 0 1 2 3 3 3 3 4 4 4
0 marks · CSE 21, TT1 Set A · Bank p.14 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
To enumerate all maximum-length LCSs, backtrack all optimal predecessor cells instead of storing one path. On a match go diagonal; on mismatch follow every neighbor whose value equals the current optimum. Store results in a set to remove duplicates. For the supplied example the stated results are ALGM and ALGT.
Q28
You are given the cost matrix b[] for two strings “ALGORITHM” and “ALIGNMENT”. How do you modify the LCS DP SOLUTION algorithm to find the K th highest LCS as per the user’s choice? For example, for the given k = 3, the output will be ALG; for k = 4, the output will be ALGM or ALGT. Xi \Yj ε A L G O R I T H M ε 0 0 0 0 0 0 0 0 0 0 A 0 1 1 1 1 1 1 1 1 1 L 0 1 2 2 2 2 2 2 2 2 I 0 1 2 2 2 2 3 3 3 3 G 0 1 2 3 3 3 3 3 3 3 N 0 1 2 3 3 3 3 3 3 3 M 0 1 2 3 3 3 3 3 3 4 E 0 1 2 3 3 3 3 3 3 4 N 0 1 2 3 3 3 3 3 3 4 T 0 1 2 3 3 3 3 4 4 4
0 marks · CSE 21, TT1 Set B · Bank p.14 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Enumerate distinct common subsequences by decreasing length (or DP over state plus required length), then select the user’s k-th requested length/order. For the supplied example, k=3 gives ALG; k=4 gives ALGM or ALGT, matching the question statement.
Q29
Find an optimal solution to the following knapsack instance using dynamic programming: Number of Objects N = 6, Maximum capacity M = 15 and Pi 10 5 15 7 8 18 Wi 3 4 3 5 2 6 Where Pi and Wi denotes the price and weight of the i-th product respectively.
10 marks · CSE 16-17, Final; CSE 17, Final · Bank p.15 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Maximum profit = 51; one optimal set is items {1,3,5,6}, total weight 14.
Derivation / working

0/1 recurrence: dp[i][w]=max(dp[i−1][w], Pᵢ+dp[i−1][w−Wᵢ]) when Wᵢ≤w, otherwise dp[i−1][w]. Backtracking from dp[6][15] selects items 6,5,3,1.

itemweightprofitselected
1310yes
245no
3315yes
457no
528yes
6618yes

Weight=3+3+2+6=14≤15; profit=10+15+8+18=51.

Q30
Find an Optimal Solution to the 0-1 Knapsack Problem from the following items using Dynamic Programming. Number of Items N = 5, Maximum Weight M = 10 Pi 10 3 15 7 8 Wi 3 4 2 2 1 where Pi and Wi denote the price and weight of the i-th product respectively.
5 marks · 2017-18 Akash · Bank p.15 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Maximum profit = 40. One optimal set is items {1,3,4,5}, total weight 8.
Derivation / working
itemweightprofitselected
1310yes
243no
3215yes
427yes
518yes

0/1 DP: dp[i][w]=max(dp[i−1][w], Pᵢ+dp[i−1][w−Wᵢ]) if Wᵢ≤w. Backtrack from dp[5][10] to recover the selected items.

Q31
Consider the following weight and value lists for 7 gifts: Weights 5 7 3 8 4 6 9 Values 12 15 8 9 11 20 12 Gift no 1 2 3 4 5 6 7 You have a bag of maximum capacity of weight 15kg. (a) Your task is to find the maximum value you can have taking the gifts optimally using bottom-up approach. [5] (b) Find which gifts you have picked from (a). [Show using path printing] [2] (c) Why do you think 0/1 Knapsack is a dynamic programming problem whereas the fractional Knapsack is a greedy problem? [1]
8 marks · CSE 22, Final · Bank p.15 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
(a) Maximum value = 43. (b) Pick gifts {1,5,6} with total weight 15. (c) 0/1 knapsack needs DP because items are indivisible; fractional knapsack can use a value/weight greedy rule because fractions allow an exchange argument.
Derivation / working
giftweightvalueselected
1512yes
2715no
338no
489no
5411yes
6620yes
7912no

Total weight=5+4+6=15; value=12+11+20=43.

Q32
Consider a variant of the knapsack problem in which you can accept, reject, or half-accept each item. As before, the input is a set of n items, where the ith item has size si and value vi . In case you half-accept an item si , a fraction 12 of the item with size si /2 and value vi /2 will be placed in the knapsack. Derive a Dynamic Programming solution that finds the value of the optimal solution. It suffices to define sub-problems and provide a recursive formula for the value of optimal solutions.
10 marks · CSE 20, Final · Bank p.15 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Define F(i,c)=maximum value using items i…n with remaining capacity c. Transition: max of reject F(i+1,c), accept vi+F(i+1,c−si) if si≤c, and half-accept vi/2+F(i+1,c−si/2) if si/2≤c. Base F(n+1,c)=0. Memoize states. If sizes are integral after scaling by 2, complexity is O(nC) on the scaled capacity.
Q33
Five matrices are given with their dimension: A1 = 5 × 10, A2 = 10 × 15, A3 = 15 × 5, A4 = 5 × 100, A5 = 100 × 20. Using the Dynamic programming approach for Matrix Chain Multiplication, Find: (i) The Optimal Order of Multiplication. [2] (ii) The minimum number of multiplication required for the multiplication. [8]
10 marks · CSE 16-17, Final; CSE 17, Final · Bank p.16 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Optimal order = ((A1×(A2×A3))×(A4×A5)); minimum scalar multiplications = 11,500.
Derivation / working
subchainminimum cost
A1A2750
A2A3750
A3A47500
A4A510000
A1..A31000
A2..A45750
A3..A511500
A1..A43500
A2..A511750
A1..A511500

Dimension vector p=<5,10,15,5,100,20>. Final best split is k=3: m[1,3]+m[4,5]+5·5·20 = 1000+10000+500=11500.

Q34
(i) You are given the dimension array of Matrices is {30, 70, 10, 25, 45}, where Matrix 1 is built from 1st and 2nd dimensions, Matrix 2 is built from 2nd and 3rd dimensions and so on. Now find the optimal order of multiplication of the Matrices and the minimum number of required multiplication operations using Dynamic Programming. [8] (ii) What is the formula to calculate LCS value in a cell using Bottom Up DP? [2]
10 marks · 2017-18 Akash · Bank p.16 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
(i) Minimum scalar multiplications = 45,750; optimal parenthesization = ((A1×A2)×(A3×A4)). (ii) Bottom-up LCS: if X[i−1]=Y[j−1], c[i][j]=c[i−1][j−1]+1; else c[i][j]=max(c[i−1][j],c[i][j−1]).
Derivation / working
m[i,j]cost
m1221000
m2317500
m3411250
m1328500
m2442750
m1445750

Dimension vector p=<30,70,10,25,45>; final split k=2.

Q35
Consider the shapes following matrices: [2 × 4], [4 × 6], [6 × 40], [40 × 8]. 1. Find the minimum number of scaler multiplication required to find the Matrix Multi- plication result. 2. What is the optimum order of that multiplication? Show using brackets. Further explanation is unnecessary.
10 marks · CSE 19, Final · Bank p.16 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Minimum scalar multiplications = 1168. Optimal order: (((A1×A2)×A3)×A4).
Derivation / working

Dimensions p=<2,4,6,40,8>.

Subchainbest cost
A1A22·4·6=48
A2A34·6·40=960
A3A46·40·8=1920
A1..A3528
A2..A42112
A1..A41168

The final split is after A3: cost m[1,3]+m[4,4]+2·40·8 = 528+640 = 1168.

Q36
Find an optimal parenthesization of a matrix-chain product whose sequence of dimensions is ⟨5, 10, 3, 12, 5, 50, 6⟩. You have to show the value of both m and s tables.
10 marks · CSE 20, Final · Bank p.16 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Minimum cost = 2010; optimal parenthesization = ((A1×A2)×((A3×A4)×(A5×A6))).
Derivation / working

m table (minimum costs):

j=1j=2j=3j=4j=5j=6
i=1015033040516552010
i=2—036033024301950
i=3——01809301770
i=4———030001860
i=5————01500
i=6—————0

s table (best split k):

j=1j=2j=3j=4j=5j=6
i=1—12242
i=2——2222
i=3———344
i=4————44
i=5—————5
i=6——————

Use m[i,j]=minₖ(m[i,k]+m[k+1,j]+p[i−1]p[k]p[j]).

Q37
The m and p tables have been calculated to solve the MCM problem for the matrix sequence: A1 (5 × 7), A2 (7 × 10), A3 (10 × 7), and A4 (7 × 5). Write the pseudocode to find the number of ways to multiply the matrices. Apply your proposal and find the answer. Cost table m[i, j] (sub-label = resulting dim): Split index p[i, j]: m[i, j] j=1 j=2 j=3 j=4 p[i, j] j=1 j=2 j=3 j=4 i=1 0(5×7) 350(5×10) 700(5×7) 875(5×5) i=1 — 1 2 1 or 3 i=2 — 0(7×10) 490(7×7) 700(7×5) i=2 — — 2 2 i=3 — — 0(10×7) 350(10×5) i=3 — — — 3 i=4 — — — 0(7×5) i=4 — — — — Note: the original scan shows m[1, 4]=872 (arithmetic slip); the correct DP value is 875 as shown above.
10 marks · CSE 21, Final · Bank p.16 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
The number of optimal parenthesizations is 2 because p[1,4] allows two optimal split positions (k=1 or k=3). Count recursively: ways(i,i)=1; ways(i,j)=Σ ways(i,k)·ways(k+1,j) over every k that attains m[i,j].
Correction / source note
The bank itself records the corrected cost m[1,4]=875; retain that correction.
Q38
Consider a sequence of four matrices A1 , A2 , A3 , and A4 with the following dimensions: • A1 : 5 × 10 • A2 : 10 × 3 • A3 : 3 × 12 • A4 : 12 × 5 Your goal is to find the most efficient way to compute the product A1 A2 A3 A4 by minimizing the total number of scalar multiplications. (a) Construct the complete m[i, j] table using the bottom-up DP approach to find the minimum number of scalar multiplications. Show your calculations for at least three non-trivial cells. [5] (b) Construct the corresponding s[i, j] table (split table) and use it to determine the optimal parenthesization of the matrix chain. Trace the recursive recovery process. [2] (c) Suppose we want to find the least efficient (maximum scalar multiplications) way to multiply the matrices instead of the minimum. Synthesize the necessary changes to the recurrence relation and the initialization to achieve this. [3]
10 marks · CSE 23, Final · Bank p.16 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
(a) Minimum cost = 405. (b) Optimal parenthesization = ((A1×A2)×(A3×A4)). (c) To find the least-efficient order, replace min by max and initialize non-diagonal states to −∞; maximum cost here is 1260.
Derivation / working
m tablej1j2j3j4
i10150330405
i2—0360330
i3——0180
i4———0
s tablej1j2j3j4
i1—122
i2——22
i3———3
i4————

Examples: m12=5·10·3=150; m34=3·12·5=180; m14=min{0+330+5·10·5, 150+180+5·3·5, 330+0+5·12·5}=405.

Q39
You’re given a list of coins as {1, 2, 3, 5} and an amount of 6. The coins can be used infinitely. (a) Find the minimum number of coins to make the amount using coin change in recursive approach and show the tree diagram. Show which coins are needed. [7] (b) What is its complexity? [1] (c) How would you handle if the coins could be used multiple but limited times? (e.g., 1 for 3 times, 2 for 5 times, etc.) [2]
10 marks · CSE 22, TT2 · Bank p.17 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
(a) Minimum coins for 6 is 2: either 3+3 or 5+1. (b) naive recursion is exponential; memoized DP is O(A·C). (c) limited copies require the remaining count of each coin to be part of the state.
Derivation / working
F(6)
├─ use1 → F(5)
├─ use2 → F(4)
├─ use3 → F(3) → use3 → F(0)  => 2 coins
└─ use5 → F(1) → use1 → F(0)  => 2 coins

Recurrence for unlimited coins: F(a)=1+min(F(a−c)) over c≤a, F(0)=0. Without memoization, repeated subproblems create an exponential tree. With memoization there are A states and C coin transitions per state.

For bounded copies, use F(amount, counts[]) or bounded-knapsack DP, decrementing a denomination count when used.

Q40
(a) You are a delivery driver starting from your depot with a van that has a fuel range of 500 km. There are 10 packages to deliver, each located at a destination at a known distance from the depot. You may deliver them in any order you choose, returning to the depot after each delivery is not required; you travel directly from one delivery location to the next. However, there is a catch: delivering a heavy package reduces your remaining range permanently. Specifically, each time you deliver a heavy package, your van’s fuel efficiency drops, reducing your remaining range by 20 km. Importantly, only the heavy packages delivered before a given delivery count toward the range reduction for that leg; meaning the sequence in which you deliver packages directly affects whether you have enough fuel to reach each subsequent destination. Your goal is to maximize the number of deliveries completed without running out of fuel before finishing your chosen sequence. Analyze whether the given problem satisfies the two fundamental properties required for dynamic programming: Overlapping Subproblems, Optimal Substructure. Based on your analysis, determine if dynamic programming is applicable. Justify your answer with appropriate reasoning. [4] (b) If mask represents the set of cities already visited, and j is the city we’re currently at, what does new_mask represent in terms of our path so far? Explain with one example. Also, write the equation for finding the new mask using bitmasking for solving the travelling salesman problem. [3] (c) Consider coin denominations {1, 2, 3, 4} with unlimited supply. You need to make a target amount of 10. Let dp[i][j] represent the minimum number of coins needed to make amount j using only the first i coin types (coins[0] through coins[i-1]). Construct the complete 2D DP table. Show all intermediate calculations. What is the minimum number of coins needed to make amount 10 using all available coins using backtracking? [3]
10 marks · CSE 23, Final · Bank p.17 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
(a) DP is applicable if state includes all history that affects future feasibility (e.g. visited set/current position/number of heavy deliveries/remaining range), producing overlapping states and optimal substructure. (b) new_mask = mask | (1<<next); it adds the next city to visited. (c) For coins {1,2,3,4}, amount 10, minimum is 3 coins, e.g. 4+4+2.
Derivation / working
For the 2D coin DP: dp[i][0]=0; dp[0][j>0]=∞; dp[i][j]=min(dp[i−1][j],1+dp[i][j−coin[i−1]]) for unlimited supply.
Q41
We have followed the steps (up to D3 ) of the Floyd-Warshall algorithm for finding the weight of all-pair shortest paths in the following graph. You have to calculate the step D4 . If we completely follow the steps (i.e. calculating up to D4 ), may we get a solution? Briefly justify your answer.
10 marks · CSE 20, Final · Bank p.18 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Diagram-dependent. Apply Floyd–Warshall step D4 by allowing vertex 4 as the newest intermediate: D4[i][j]=min(D3[i][j],D3[i][4]+D3[4][j]). After all vertices have been allowed, the table is the APSP solution unless a negative cycle exists.
Correction / source note
The text layer does not preserve every visual edge/weight/table cell. The original PYQ page scan is embedded below this topic, so use that scan for the exact diagram.
Complete previous-year question map

Red-Black Trees, Binomial Heaps & Strassen’s Algorithm

All bank questions for this topic are represented. Question stays visible; answer stays collapsed. Clear duplicates are consolidated while every occurrence and alternate wording is retained.

PYQ4 bank questions
Q1
State the limitations of a binary search tree.
1 mark · CSE 21, Final · Bank p.18 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplementAn ordinary BST can become highly unbalanced (e.g., sorted insertion), making height n and search/insert/delete O(n). A balanced tree such as a red-black tree keeps height O(log n).
Q2
The insertion complexity of an element in a Heap data structure is O(N log N ), where N is the number of elements currently on the heap. True or False? If False, write the correct statement.
2 marks · CSE 19, Final · Bank p.18 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplementFalse. In a binary heap with N elements, insertion appends the item and percolates it up through at most the heap height, so insertion is O(log N), not O(N log N).
Q3
(a) You’re given two matrices as below: [6] 1 2 −2 1 0 0 1     −1 2 0 0 1 1   0 S= ·    0 1 −2 −1 0 −1 0   3 0 −1 1 0 1 −1 Multiply the two matrices using Strassen’s algorithm. [Use given Si , Pi relations: S1 = B12 − B22 , S2 = A11 + A12 , S3 = A21 + A22 , S4 = B21 − B11 , S5 = A11 + A22 , S6 = B11 + B22 , S7 = A12 − A22 , S8 = B21 + B22 , S9 = A11 − A21 , S10 = B11 + B12 and P1 = A11 · S1 , P2 = S2 · B22 , P3 = S3 · B11 , P4 = A22 · S4 , P5 = S5 · S6 , P6 = S7 · S8 , P7 = S9 · S10 .] (b) How does Strassen’s algorithm help to reduce the time complexity of matrix multipli- cation compared to the divide and conquer algorithm? Give a comparative analysis. [3] (c) Why do we need algorithms even if we have an unnatural computer having power to execute instantaneously and infinite memory? [1]
10 marks · CSE 22, Final · Bank p.18 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
(a) As printed in the supplied PYQ, the first matrix is 4×3 and the second is 4×4, so their product is undefined (inner dimensions 3 and 4 do not match). (b) Strassen reduces 8 half-size recursive multiplications to 7, giving O(n^log₂7)≈O(n^2.807) instead of O(n³). (c) Algorithms are still needed to specify correctness/termination and solve the computational problem; unlimited speed/memory does not define the procedure.
Correction / source note
This is a genuine source inconsistency visible in the PYQ scan. Applying the supplied Strassen S/P formulas would require compatible square blocks; silently padding or inventing a missing column would change the question.
Q4
Assume the following matrices. 4 0 0 −1 3 2     B = 0 1 0 A= 5 7 −3     0 0 1 11 15 −2 (a) On which type of problems, we can apply divide and conquer approach? [2] (b) Use divide and conquer approach to multiply these matrices. [6] (c) How Strassen’s algorithm optimizes the divide and conquer method for matrix multi- plication. [2]
10 marks · CSE 22, TT1 · Bank p.19 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
(a) Divide-and-conquer applies when a problem can be split into smaller similar subproblems, solved independently/recursively, and efficiently combined. (b) Multiplying in the displayed order B·A gives the matrix below. (c) Strassen reduces the block multiplications from 8 to 7.
Derivation / working
B·A =
[-4  12   8]
[ 5   7  -3]
[11  15  -2]

Here B=diag(4,1,1), so B·A simply multiplies the first row of A by 4. A recursive block method can pad 3×3 matrices to 4×4 with zeros before dividing into quadrants. Standard block multiplication has T(n)=8T(n/2)+Θ(n²)=Θ(n³); Strassen has 7T(n/2)+Θ(n²)=Θ(n^2.807).

Correction / source note
The question does not explicitly state whether to compute B·A or A·B. The matrices are printed in the order B then A, so this answer follows that displayed order; A·B would instead scale A’s columns by 4,1,1.
Complete previous-year question map

Graph Algorithms (Traversal, Shortest Path, MST, Connectivity)

All bank questions for this topic are represented. Question stays visible; answer stays collapsed. Clear duplicates are consolidated while every occurrence and alternate wording is retained.

PYQ39 bank questions
Q1
What is a bipartite graph?
1 mark · CSE 23, Final · Bank p.19 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
A bipartite graph has vertices partitioned into two disjoint sets U,V such that every edge joins U to V; equivalently, it is 2-colorable.
Q2
What are the Bridges and Articulation Points in a connected graph?
2 marks · CSE 19, Final · Bank p.19 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
A bridge is an edge whose removal increases the number of connected components. An articulation point is a vertex whose removal (with incident edges) increases components.
Q3
What are the Bridges and Articulation Points in a connected graph? Indicate them in a graph. Also: “DFS can be used to find the shortest path between 2 nodes in an unweighted graph when a special condition is met.” What is that special condition? Give an example.
4 marks · CSE 19, TT1 · Bank p.19 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Bridge/articulation definitions as above. DFS gives shortest paths in an unweighted graph only when the DFS tree path happens to be a shortest path (e.g. a graph with a unique path between the two vertices, such as a tree); BFS is the general shortest-path method for unweighted graphs.
Q4
(a) What do you understand by articulation point and bridge? [1] (b) “Back-edges from any node in the subtree of a node to its ancestors restrict it from becoming an articulation point”. Justify with example. [2] (c) You’re given the following graph. Find the articulation points in this graph using DFS. Assume 0 is the root node. [4] (d) Find the articulation bridges on the same graph. (You may use the previous traversal data) [3]
10 marks · CSE 22, Final · Bank p.19 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
(a) An articulation point is a vertex whose removal increases the number of connected components; a bridge is an edge whose removal does so. (b) A back-edge from a child subtree to an ancestor lowers low[child], showing an alternate route that can prevent the parent from being an articulation point. (c) Articulation points in the shown graph are {0,1,2,4}. (d) Bridges are (1,7), (1,0), (2,4), (4,9).
Derivation / working

DFS rule: for non-root u, if some child v has low[v]≥disc[u], then u is an articulation point; tree edge (u,v) is a bridge if low[v]>disc[u]. Root 0 is an articulation point when its DFS structure separates the left branch from the rest.

Q5
Draw a graph where BFS will work faster than DFS to reach a node from a root node. Your example graph should be unweighted. It can be either directed or undirected.
2 marks · CSE 19, Final · Bank p.20 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Example: root r connected directly to target t plus a very long chain starting from another neighbor. BFS discovers t at depth 1 immediately; DFS may explore the long chain first.
Q6
Traverse the following graph in DFS order starting from a.
5 marks · CSE 20, Final · Bank p.20 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Assuming the natural left-to-right/alphabetic neighbor order in the drawing, DFS from a visits a → b → c → d → e → f → g → i → k. Vertices h and j are not reachable from a along the shown arrow directions.
Derivation / working

Trace: a→b→c→d, backtrack to a, then a→e→f (f→d already visited), back to e→g→i→k. DFS order can change if a different adjacency-list order is chosen.

Q7
Find a valid topological sorting order for the following graph, a → b indicates that a must appear BEFORE b in a valid order.
5 marks · CSE 19, Final; CSE 19, TT1 · Bank p.20 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
One valid topological order is A → C → D → B.
Derivation / working

The shown precedence edges are A→B, A→C, C→B, C→D and D→B. A must precede C; C must precede D; and A,C,D must all precede B. Thus A,C,D,B satisfies every directed edge.

Q8
Check if the following graph has an Euler path. If there is, use Fleury’s algorithm to find the Euler’s path. Show each step.
10 marks · CSE 19, Final · Bank p.20 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Yes. Exactly two vertices have odd degree: 2 and 4, so an Euler path exists from one to the other. One Fleury path is 2→1→3→2→4→5→6→7→4.
Derivation / working

At each step Fleury avoids a bridge unless it is the only remaining incident edge. The path uses every edge exactly once and ends at the other odd-degree vertex.

Q9
Simulate Fleury’s algorithm in the following graph to find the Euler’s path. Start Node is 1.
6 marks · CSE 19, TT1 · Bank p.21 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Starting at node 1, one Euler path is 1→3→4→1→2→5→6→2.
Derivation / working

Odd-degree vertices are 1 and 2, so an Euler trail must start at one and end at the other. Fleury first consumes the left triangle without taking bridge 1–2 too early; then crosses 1–2 and consumes the right triangle.

Q10
Find all the Strongly Connected Components in the following directed graph using an algorithm of your preference. Time complexity should not exceed O(N ).
10 marks · CSE 19, Final · Bank p.21 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
The strongly connected components are {1,2,3} and {4,5,6,7}.
Derivation / working

Using Tarjan: DFS assigns discovery/low values and keeps active vertices on a stack. The cycle 1→2→3→1 forms one SCC. Edge 2→4 reaches the second cycle 4→5→6→7→4, which forms the other SCC. Tarjan/Kosaraju runs in O(V+E), which is linear in the graph size.

Q11
Suppose adjacency matrix and adjacency list take asymptotically equal space of Θ(n2 ) to store a graph of n vertices in which every vertex has Θ(n) neighbors. — True or False? Briefly justify.
2 marks · CSE 22, Final · Bank p.21 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
True. A dense graph where each of n vertices has Θ(n) neighbors has Θ(n²) edges, so adjacency list space Θ(V+E)=Θ(n²), same asymptotic space as the matrix.
Q12
“Dijkstra qualifies as a greedy algorithm”, why?
2 marks · CSE 19, Final · Bank p.21 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedDijkstra is greedy because at every step it permanently selects the unsettled vertex with minimum tentative source distance. With non-negative edge weights, no later path through unsettled vertices can improve that settled distance.
Q13
Draw a graph with negative edges where Dijkstra will be able to find the shortest path between any pair of nodes.
2 marks · CSE 19, Final · Bank p.21 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Yes, Dijkstra can still succeed on a particular graph containing negative edges if those edges never create a shorter path to an already settled vertex. Example: s→a=2, s→b=5, b→a=−1; shortest s→a remains 2, so Dijkstra happens to be correct. It is not guaranteed in general.
Q14
When the Dijkstra algorithm will fail to find the shortest path between 2 nodes in a graph with negative edges? Discuss with an example.
5 marks · CSE 19, TT1 · Bank p.21 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Dijkstra fails when a negative edge can create a shorter route to a vertex after that vertex has already been settled.
Derivation / working

Example: s→a=2, s→b=5, b→a=−10. Dijkstra settles a first with distance 2. Later b gives distance 5−10=−5 to a, but a was already finalized. Thus Dijkstra returns the wrong result. With a reachable negative cycle, a finite shortest path may not exist.

Q15
Bellman-Ford will be able to find the shortest path between 2 nodes of a weighted-connected graph if all the edges are positive weighted. True or False? If False, write the correct statement.
2 marks · CSE 19, Final · Bank p.21 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
True: Bellman–Ford works with positive weights too. Its extra strength is that it also handles negative edges and can detect reachable negative cycles.
Q16
Compare the Dijkstra’s algorithm and Bellman-Ford algorithm in finding shortest path for a graph.
4 marks · 2017-18 Akash · Bank p.22 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Dijkstra: requires non-negative edges; greedy; O((V+E)logV) with heap. Bellman–Ford: supports negative edges, detects negative cycles; O(VE).
Q17
Find the shortest path from vertex A to H using Dijkstra’s Algorithm. Show all steps.
10 marks · CSE 16-17, Final; CSE 17, Final · Bank p.22 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Shortest A→H distance = 19. One shortest path is A→B→C→D→H.
Derivation / working
settledd(A)d(B)d(E)d(C)d(F)d(G)d(D)d(H)
A046∞∞∞∞∞
B046810∞∞∞
E04689∞∞∞
C04689∞11∞
F046891011∞
G04689101120
D04689101119
H04689101119

Parents on one shortest route: B←A, C←B, D←C, H←D.

Q18
Find the shortest path of the following graph using Dijkstra’s Algorithm. Use the node a as the source and z as the destination.
10 marks · CSE 21, Final · Bank p.22 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Shortest a→z distance = 13; path a→c→b→d→e→z.
Derivation / working
settledabcdez
a042∞∞∞
c0321012∞
b032812∞
d03281014
e03281013
z03281013

Parent chain: z←e←d←b←c←a.

Q19
If an edge of a graph has larger weight than every other edge, it cannot be part of any MST. True or False? (Variant in CSE 20 Final and CSE 21 Final.)
2 marks · CSE 23, Final · Bank p.22 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
False. A globally largest-weight edge can still belong to an MST if it is the only edge connecting a cut/bridge.
Q20
If all edges of a connected graph G have different weights, then the minimum spanning tree of G is unique. True or False?
2 marks · CSE 21, Final · Bank p.22 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplementTrue. If all edge weights are distinct, the MST is unique. By the cut property, each cut has a unique lightest crossing edge, so Kruskal/Prim cannot face a weight tie that could lead to a different MST of the same weight.
Q21
Minimum spanning trees are used in many applications. Consider a connected graph G with edges that are labeled by positive weights. Show that if all edges have different weights, then the minimum spanning tree of G is unique.
5 marks · CSE 20, Final · Bank p.22 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Proof: assume two different MSTs T1,T2. Let e be the lightest edge in T1\T2. Adding e to T2 forms a cycle containing some f∈T2\T1. Distinct weights and choice of e imply w(e)<w(f); replacing f by e makes T2 lighter, contradiction. Hence the MST is unique.
Q22
Kruskal is better than Prims for finding the Minimum Spanning Tree within a graph in terms of time-complexity. True or False? If False, write the correct statement.
2 marks · CSE 19, Final · Bank p.22 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
False as a universal statement. With adjacency lists + heap, Prim is O(E log V); Kruskal is O(E log E)≈O(E log V). Which is faster depends on graph representation/density and implementation.
Q23
Why should we classify Prim’s or Kruskal’s solution as a greedy approach?
2 marks · CSE 20, Final · Bank p.22 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Both are greedy because they repeatedly choose the currently cheapest safe edge: Prim chooses the lightest crossing edge from the current tree; Kruskal chooses the lightest edge that does not create a cycle.
Q24
What will be the worst-case complexity of Kruskal’s algorithm to find the MST of a graph having N vertices?
2 marks · CSE 20, Final · Bank p.22 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Kruskal: sort E edges O(E log E), plus near-linear DSU. In terms of N vertices alone, E can be Θ(N²), giving O(N² log N) worst case for a dense simple graph.
Q25
There is an O(n2 ) time algorithm for finding the minimum spanning tree in a graph. [True/False]
1 mark · CSE 21, Final · Bank p.23 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
True. Prim with an adjacency matrix runs in O(n²).
Q26
Explain the application of the MST in the Cable TV Industry or SUST Water Supply Network to minimize resources like length of cable or pipe.
5 marks · CSE 21, Final · Bank p.23 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Model junctions/houses as vertices and candidate cable/pipe links as weighted edges (length/cost). Compute an MST so every location is connected with minimum total link cost and no cycle.
Derivation / working

In Cable TV, the MST minimizes total cable needed for the backbone. In a water-supply layout, it minimizes total pipe length/cost when only connectivity is required. Prim or Kruskal can be used.

Important limitation: a real network may need redundancy, capacity, pressure or reliability constraints; then a plain MST may not be sufficient.

Q27
Consider the weighted graph in the following figure. Apply Kruskal’s algorithm to find out the minimum-cost spanning tree. Show the steps.
10 marks · CSE 16-17, Final; CSE 17, Final · Bank p.23 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Kruskal MST cost = 52. Selected edges: (a,e,2),(e,f,5),(b,c,6),(f,h,7),(b,g,8),(a,b,11),(a,d,13).
Derivation / working

Process edges in ascending weight. Add an edge only if DSU says its endpoints are in different components; skip edges that form a cycle. After 7 selected edges for 8 vertices, stop.

Q28
Use the Kruskal algorithm to find the Minimum Spanning Tree of the following weighted- connected graph.
5 marks · CSE 19, Final · Bank p.23 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
MST cost = 8. One MST: (1,2,1),(3,5,1),(1,5,2),(1,6,2),(5,4,2).
Derivation / working

Sorted edge weights start 1,1,2,2,2,3,3,3. The five listed edges connect all six vertices without a cycle, so Kruskal stops.

Q29
Based on Figure: Graph 1, answer questions (a) and (b), and based on Figure: Graph 2, answer question (c). (a) Apply Kruskal’s Algorithm to the given graph (Graph 1) to determine its Minimum Spanning Tree (MST) showing all intermediate steps. [5] (b) Explain whether the graph (Graph 1) can produce multiple minimum spanning trees, providing justification for your answer. [2] (c) Oggy, Deedee, and Marky are three close friends living in the same house in different rooms. Oggy lives in room A, Deedee lives in room B, and Marky lives in room D. Oggy wants to visit all of his friends exactly once and then return to his own room. Oggy has a maximum stamina of 20 units. He wants to complete his travel with minimum stamina cost. If g(i, S) is the cost function, where i is the current room and S is the set of remaining rooms to visit, find the recurrence relation to solve Oggy’s problem. Write all possible recurrence relations from the starting point to the base case. [3]
10 marks · CSE 23, Final · Bank p.23 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
(a) Graph 1 MST cost = 21: CG(1), CD(2), AB(3), CF(4), FE(5), BC(6). (b) The MST is unique because all relevant edge weights are distinct. (c) For Graph 2, the optimal tour cost is 15.
Derivation / working

Kruskal order for Graph 1 selects 1,2,3,4,5,6-weight edges above; any next edge would create a cycle or is heavier.

g(A,{B,D}) = min( 3 + g(B,{D}), 5 + g(D,{B}) )
g(B,{D}) = 7 + g(D,∅) = 7 + 5 = 12
g(D,{B}) = 7 + g(B,∅) = 7 + 3 = 10
g(B,∅) = w(B,A)=3
g(D,∅) = w(D,A)=5
Therefore g(A,{B,D}) = min(15,15) = 15.

Both A→B→D→A and A→D→B→A cost 15, within stamina 20.

Q30
Find the MST using Prim’s and Kruskal’s methods for the following graph G(V, E) where V = {a, b, c, d, e, f } and E(u, v, w) = {(a, b, 7), (a, e, 5), (b, c, 5), (b, d, 2), (c, d, 3), (c, f, 7), (d, e, 1), (d, f, 4), (e, f, 3)}.
10 marks · CSE 21, Final · Bank p.24 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Both Prim and Kruskal give MST cost = 14. One MST is de(1), bd(2), cd(3), ef(3), ae(5).
Derivation / working

Kruskal: select de1, bd2, cd3, ef3; skip df4 (cycle); select ae5. Total 14.

Prim (start a): choose ae5 → ed1 → db2 → dc3 → ef3. Same total 14.

Q31
Construct a Graph with 8 Nodes and 28 Directed Edges. Now, find first 7 Fibonacci Primes and use each of them four times to give the weight of the edges. Then take a graph algorithm which was not in the syllabus of any past courses but in this course. Then exemplify that algorithm with this graph.
10 marks · CSE 16-17, Final; CSE 17, Final · Bank p.24 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
For 8 vertices there are 8·7/2=28 undirected pairs, or 56 possible directed arcs without self-loops; the request for 28 directed edges can choose any 28. First Fibonacci primes are 2,3,5,13,89,233,1597. Assign each four times to 28 edges, then demonstrate a graph algorithm newly taught in the course, e.g. Dijkstra/Prim/Kruskal depending the intended syllabus.
Correction / source note
The exact constructed graph is not unique; any construction satisfying the counts and weights is acceptable.
Q32
Which statements about a tree are correct? (A) It is connected; (B) It is acyclic; (C) Every node is reachable from every other node; (D) It must contain at least one cycle.
2 marks · CSE 23, TT1 · Bank p.24 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
A, B and C are true; D is false.
Q33
A spanning tree of a graph must contain: (A) Only the cheapest edges; (B) All edges of the original graph; (C) All vertices of the original graph; (D) Exactly one cycle.
2 marks · CSE 23, TT1 · Bank p.24 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Correct option: C — all vertices of the original graph. A spanning tree also has V−1 edges and no cycle, but those are not listed as the single requested condition.
Q34
Which statements about a spanning tree are correct? (A) It includes all vertices of the original graph; (B) It has exactly N − 1 edges; (C) It is connected; (D) It contains no cycles.
2 marks · CSE 23, TT1 · Bank p.24 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
A, B, C and D are all true.
Q35
While processing an edge in Kruskal’s Algorithm, which statements are correct? (A) If endpoints are in different components, add the edge; (B) If endpoints are in the same component, skip the edge; (C) Adding an edge between the same component creates a cycle; (D) Every edge in sorted order must be selected.
2 marks · CSE 23, TT1 · Bank p.24 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
A, B and C are true; D is false.
Q36
In Kruskal’s Algorithm, edges are processed in: (A) Random order; (B) Decreasing order of weight; (C) Increasing order of weight; (D) Alphabetical order.
2 marks · CSE 23, TT1 · Bank p.24 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Correct option: C — increasing order of weight.
Q37
In DSU, what does find(x) return? (A) The parent of every node; (B) The rank of node x; (C) The representative (root) of the set containing x; (D) The size of the graph.
2 marks · CSE 23, TT1 · Bank p.24 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Correct option: C — the representative/root of the set containing x.
Q38
Which statements about path compression are correct? (A) During find, nodes on the path may point directly to the root; (B) It helps make future find operations faster; (C) It flattens the structure over time; (D) It separates one set into multiple sets.
2 marks · CSE 23, TT1 · Bank p.24 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
A, B and C are true; D is false.
Q39
Which statements about the parent[] array are correct? (A) It stores the parent of each node; (B) If parent[x] == x, x is a root; (C) It stores exact height of the tree; (D) It helps identify the representative of a set.
2 marks · CSE 23, TT1 · Bank p.24 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
A, B and D are true; C is false because parent[] does not necessarily store exact tree height.
Complete previous-year question map

Network Flow

All bank questions for this topic are represented. Question stays visible; answer stays collapsed. Clear duplicates are consolidated while every occurrence and alternate wording is retained.

PYQ11 bank questions
Q1
Define the term “maximum flow” in the context of flow networks.
1 mark · CSE 21, Final · Bank p.25 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedA maximum flow is a feasible s→t flow satisfying capacity constraints and flow conservation whose total value |f| (net flow leaving s / entering t) is as large as possible.
Q2
Key the differences between the Ford-Fulkerson method and the Edmonds-Karp algorithm for solving the maximum flow problem.
1 mark · CSE 21, Final · Bank p.25 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedFord–Fulkerson is a method that may choose any augmenting path. Edmonds–Karp is Ford–Fulkerson with BFS, always choosing a minimum-edge residual augmenting path. Edmonds–Karp has O(VE²) worst-case time independent of capacity magnitudes; arbitrary Ford–Fulkerson can depend on capacities and path choices.
Q3
“Edmonds-Karp’s solution is more greedy than the Ford-Fulkerson’s in the search of an augmenting path”. — Explain using proper example.
5 marks · CSE 20, Final · Bank p.25 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Edmonds–Karp is Ford–Fulkerson with a specific path rule: choose a residual s→t path with the fewest edges using BFS. Arbitrary Ford–Fulkerson may choose any augmenting path.
Derivation / working

Example: suppose residual paths s→a→t and s→b→c→d→t both exist. Edmonds–Karp always chooses the 2-edge path first; generic Ford–Fulkerson is allowed to choose the 4-edge path. This disciplined BFS choice makes shortest-path distances nondecreasing and yields the polynomial bound O(VE²).

Q4
What is Bipartite Matching?
2 marks · CSE 20, Final · Bank p.25 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedA matching is a set of graph edges with no shared endpoints. A maximum bipartite matching contains the largest possible number of such edges between the two partitions.
Q5
How could we solve Maximum Bipartite Matching Problem using Ford Fulkerson Algorithm?
2 marks · 2017-18 Akash; CSE 19, Final · Bank p.25 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedCreate source s connected to every left-part vertex with capacity 1; direct each original bipartite edge left→right with capacity 1; connect every right vertex to sink t with capacity 1. Run max flow. Every unit-flow left→right edge corresponds to one matched pair; max-flow value equals maximum matching size.
Q6
Find the Maximum Flow from the following graph using Ford Fulkerson Algorithm, where the source is S and destination is B.
10 marks · 2017-18 Akash · Bank p.25 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Maximum flow from S to B = 14.
Derivation / working
augmenting pathbottleneckcumulative flow
S→A→B11
S→C→B56
S→A→C→B28
S→D→B614

No more S→B residual path can increase flow: A→B is saturated, A→C is saturated, S→C is saturated, and D→B is saturated. Thus flow 14 is maximum.

Q7
Consider the following network. Run the Ford-Fulkerson algorithm to compute a max-flow. For each iteration give the residual network and mark the path you choose for augmentation.
10 marks · CSE 16-17, Final; CSE 17, Final · Bank p.25 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Maximum flow A→F = 18.
Derivation / working
augmenting pathbottleneckcumulative
A→B→C→F1010
A→D→E→F616
A→D→C→E→F218

The sink F has incoming capacity C→F 10 plus E→F 8 = 18, giving an upper bound of 18; the constructed flow reaches it, so it is maximum.

Q8
Apply Ford Fulkerson algorithm to find the Maximum Flow permitted by the following network. Source node is 1 and the sink node is 6.
10 marks · CSE 19, Final · Bank p.25 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Maximum flow from 1 to 6 = 10.
Derivation / working
pathbottleneckcumulative
1→2→4→655
1→2→5→627
1→3→5→629
1→3→2→5→6110

A cut {1,2,3} | {4,5,6} has capacity 2→4 (5) + 2→5 (3) + 3→5 (2) = 10, proving optimality by max-flow/min-cut.

Q9
(a) Explain how the Max-Flow Min-Cut theorem supports the Ford-Fulkerson method. [2.5] (b) Prove that the Ford-Fulkerson algorithm always produces a maximum flow assuming capacities are integers. [2.5] (c) Apply the Ford-Fulkerson algorithm to find the maximum flow permitted by the following network. The source node is 1 and the sink node is 6. [5]
10 marks · CSE 22, Final · Bank p.26 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
(a) Max-flow/min-cut says a feasible flow is maximum exactly when its value equals the capacity of some s–t cut. (b) With integer capacities, each augmentation increases flow by an integer ≥1, so Ford–Fulkerson terminates; when no augmenting path remains, residual reachability defines a cut with capacity equal to the flow. (c) For the shown network, maximum flow = 10.
Derivation / working

For part (c), one augmentation sequence is 1→2→4→6:5; 1→2→5→6:2; 1→3→5→6:2; 1→3→2→5→6:1. Total 10. The cut {1,2,3}|{4,5,6} also has capacity 10.

Q10
Consider the following graph. (a) What are the properties of the source and the sink nodes in a flow network? [2] (b) Find the maximum flow of the network. [6] (c) How would you handle if there are multiple sources and sinks? [2]
10 marks · CSE 22, TT1 · Bank p.26 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
(a) Source S is the origin of net flow; sink T is the destination. Intermediate vertices obey flow conservation. (b) Maximum flow = 23. (c) For multiple sources/sinks, add a super-source and super-sink.
Derivation / working

One flow decomposition: S→A→T =5; S→A→B→T =5; S→C→B→T =3; S→C→D→T =10. Total 23.

Cut {S,A,B,C}→{D,T}: A→T(5)+B→T(8)+C→D(10)=23, so no flow can exceed 23.

Connect super-source to each original source and each original sink to super-sink with capacities equal to supply/demand or sufficiently large values.

Q11
Consider the following flow network and initial flow f (representing f : c as edge-label where f is the flow, c is the capacity). Draw the residual graphs Gf of G, find all augmenting paths with the fewest possible edges after performing the augmentation. What is the value of the resulting (final) flow? How could you solve the maximum-bipartite matching problem using the maximum-flow-network solution?
10 marks · CSE 20, Final · Bank p.27 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Initial flow value is 25. Edmonds–Karp can augment twice by 1, giving final maximum flow = 27.
Derivation / working
BFS residual pathbottleneckflow value
s→3→2→5→t126
s→4→3→2→5→t127

The reverse residual edges 3→2 and 4→3 cancel earlier flow on 2→3 and 3→4, allowing more flow through 2→5→t. After the second augmentation no residual s→t path remains.

For bipartite matching: source→left vertices capacity1; original left→right edges capacity1; right vertices→sink capacity1. The max-flow value equals maximum matching size; unit-flow left→right edges are the matches.

Complete previous-year question map

Backtracking & Branch-and-Bound

All bank questions for this topic are represented. Question stays visible; answer stays collapsed. Clear duplicates are consolidated while every occurrence and alternate wording is retained.

PYQ26 bank questions
Q1
Name two name of problems that can be solved using branch-and-bound.
1 mark · CSE 21, Final · Bank p.27 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Examples: Traveling Salesman optimization and 0/1 Knapsack optimization.
Q2
In branch-and-bound approach, what do the branching and bounding mean?
2 marks · CSE 20, Final · Bank p.27 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedBranching splits the solution space into child subproblems/choices. Bounding computes a best-possible optimistic cost/value for a partial state; if that bound cannot beat the current best complete solution, prune the branch.
Q3
List any two applications (or algorithms) for BnB. Avoid the examples mentioned in the text.
3 marks · CSE 21, TT2 Set A · Bank p.27 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Applications include assignment problem, scheduling, integer programming, maximum clique, and knapsack/TSP variants.
Q4
List any two properties of BnB technique.
3 marks · CSE 21, TT2 Set B · Bank p.27 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
BnB explores a state-space tree, keeps an incumbent best solution, uses bounds to prune non-promising nodes, and is complete if bounding is admissible.
Q5
What are the differences between backtracking and branch-and-bound?
2 marks · CSE 16-17, Final; CSE 17, Final · Bank p.27 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedBacktracking primarily prunes infeasible constraint-satisfaction states and may enumerate feasible solutions. Branch-and-bound targets optimization: it keeps an incumbent best answer and prunes states using objective bounds, often exploring nodes best-first/BFS rather than depth-first only.
Q6
List the three principles of finding the solution of a problem using search (within the state-space).
2 marks · CSE 20, Final · Bank p.27 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
State-space search needs: a branching/generation rule, a feasibility or bounding test, and a goal/solution test (plus an exploration policy such as DFS/BFS/best-first).
Q7
Define Cost Function for the N-Puzzle problem.
2 marks · 2017-18 Akash · Bank p.27 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
For N-puzzle, a common cost is f(n)=g(n)+h(n), where g is moves so far and h estimates remaining moves.
Q8
Name any two cost-functions to evaluate the move-selection for 8-puzzle problem. Which one is better? Justify using proper example.
2 marks · CSE 20, Final · Bank p.27 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Two common 8-puzzle heuristics: number of misplaced tiles and Manhattan-distance sum. Manhattan is usually more informative because it measures how far each tile is from its goal while remaining admissible.
Q9
Name any two heuristic functions that can be used in the 8-puzzle problem.
1 mark · CSE 21, Final · Bank p.28 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Misplaced tiles and Manhattan distance.
Q10
Briefly discuss any two heuristic functions that can be used in the 8-puzzle problem.
5 marks · CSE 21, TT2 Set B · Bank p.28 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Two standard 8-puzzle heuristics are misplaced tiles and Manhattan distance.
Derivation / working

h₁ = misplaced tiles: number of nonblank tiles not in goal position.

h₂ = Manhattan distance: Σ(|currentRow−goalRow|+|currentCol−goalCol|) over nonblank tiles.

Both are admissible for standard unit-cost moves; Manhattan is usually more informative because it estimates how far each tile must travel and dominates misplaced-tiles.

Q11
Briefly explain the solution for Graph-coloring problem.
5 marks · CSE 21, TT2 Set A · Bank p.28 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedAssign colors vertex by vertex. For the current vertex, try each of m colors; a color is safe only if no already-colored adjacent vertex has it. On a safe choice recurse to the next vertex; if later impossible, undo the color and try another. When all n vertices are assigned, output the coloring. Worst-case O(m^n) assignments (plus safety-test cost).
Q12
Define chromatic number.
1 mark · CSE 21, Final · Bank p.28 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedThe chromatic number χ(G) is the minimum number of colors needed to color the vertices of G so that adjacent vertices receive different colors.
Q13
State the significance of chromatic polynomial in graph coloring.
2 marks · CSE 21, Final · Bank p.28 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
The chromatic polynomial P_G(k) counts the number of proper colorings of graph G using k colors; the smallest k with P_G(k)>0 is χ(G).
Q14
What is the key difference between vertex coloring and edge coloring?
1 mark · CSE 23, Final · Bank p.28 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Vertex coloring colors vertices so adjacent vertices differ; edge coloring colors edges so incident edges differ.
Q15
For Graph Coloring Problem what is the maximum number of nodes can be colored using two colors?
2 marks · 2017-18 Akash · Bank p.28 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
There is no fixed maximum number of vertices colorable with two colors: any bipartite graph, including arbitrarily large trees/even cycles, is 2-colorable.
Q16
“The starting node can be any node for finding Hamilton Cycle using Backtracking” — Justify the statement and explain your judgment.
2 marks · 2017-18 Akash · Bank p.28 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
For a Hamiltonian cycle, any vertex on the cycle may be chosen as the starting representation because a cycle has no intrinsic start; fixing one start removes rotational duplicates.
Q17
For N-queen problem, what is the minimum bit requirement to store the states of a board? Explain.
5 marks · CSE 20, Final · Bank p.28 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
With the standard row-by-row N-queen representation, store one column number per row: N·⌈log₂N⌉ bits. A literal N×N occupancy board would require N² bits.
Derivation / working

Because exactly one queen is placed in each row, the row is implicit. Each queen only needs its column index 0…N−1, which takes ⌈log₂N⌉ bits. Example N=8: 8·3=24 bits instead of 64 occupancy bits.

Q18
Find the solution of 5-queen problem using backtracking method.
8 marks · CSE 16-17, Final; CSE 17, Final · Bank p.28 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
One valid 5-queen solution (columns by rows, 1-indexed) is [1,3,5,2,4].
Derivation / working
r1:c1 ✓
r2:c3 ✓
r3:c5 ✓
r4:c2 ✓
r5:c4 ✓  => solution

At row r, try columns 1…5. A candidate (r,c) is safe iff no earlier queen has the same column and |c−cᵢ|≠|r−i|. If no column is safe, return to the previous row and try its next column.

Q19
Generate the State Space Tree for 5-Queen Problem, where you have to put a Queen in the first row and first column position always.
5 marks · 2017-18 Akash · Bank p.28 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
With row1 fixed at column1, one successful branch is [1,3,5,2,4].
Derivation / working
r1:c1
└─ r2:c3
   └─ r3:c5
      └─ r4:c2
         └─ r5:c4  ✓

Every omitted/crossed child is rejected immediately for same-column or diagonal conflict. The complete state-space tree branches one level per row.

Q20
Draw the state space tree for the 4-queen problem.
7 marks · CSE 21, TT2 Set B · Bank p.28 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
The 4-queen problem has two solutions: [2,4,1,3] and [3,1,4,2].
Derivation / working
row1
├─ c1 → dead end
├─ c2
│  └─ c4
│     └─ c1
│        └─ c3 ✓
├─ c3
│  └─ c1
│     └─ c4
│        └─ c2 ✓
└─ c4 → dead end

Each branch is pruned as soon as a new queen shares a column or diagonal with an earlier queen.

Q21
Draw the solution space tree for the following 8-puzzle problem.
7 marks · CSE 21, TT2 Set A · Bank p.28 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Use the 8-puzzle state-space tree by treating each board as a node and each legal blank move (U/D/L/R) as an edge. Expand only non-repeated states; with branch-and-bound/A*, order states by f=g+h.
Derivation / working

For a 7-mark drawing: write the initial board at the root, generate every legal blank move as level-1 children, continue the best/required branches, and mark repeated/dead states. Stop when the goal board is reached.

Correction / source note
The supplied PYQ bank page contains the text “following 8-puzzle problem” but does not actually show the initial/goal board. Therefore the exact source-specific tree cannot be reconstructed without inventing missing data; the original bank page is embedded below the section.
Q22
The initial position of 15-Puzzle Problem is given below. 1 2 3 4 5 6 8 9 10 7 11 13 14 15 12 Now solve the 15-Puzzle problem using Branch and Bound Technique.
7 marks · CSE 18, TT2 Set A · Bank p.28 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
The given 15-puzzle reaches the standard goal in only 3 blank moves: D → R → D.
Derivation / working
Start:             D:                R:                D (goal):
1  2  3  4        1  2  3  4        1  2  3  4        1  2  3  4
5  6  _  8        5  6  7  8        5  6  7  8        5  6  7  8
9 10  7 11        9 10  _ 11        9 10 11  _        9 10 11 12
13 14 15 12       13 14 15 12       13 14 15 12       13 14 15  _

Branch-and-bound/A* evaluates candidate boards with cost f=g+h; Manhattan distance is a standard admissible h. Here the optimal branch is immediately short.

Q23
Generate the State Space Tree for all possible different combination of length 3 using the character set {‘A’, ‘B’, ‘C’, ‘D’, ‘E’}; here you should show same character multiple times.
5 marks · 2017-18 Akash · Bank p.28 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
With 5 characters, length 3, and repetition allowed, there are 5³=125 strings.
Derivation / working

The state-space tree has 3 decision levels. Every node has five children A,B,C,D,E. Each root-to-leaf path is one output, e.g. AAA, AAB, …, EEE.

level0: ε
level1: A B C D E
level2: each has A B C D E
level3: each again has A B C D E → 125 leaves
Q24
How many expressions of 2 consonants out of 7 consonants and 1 vowel out of 3 vowels can be created?
5 marks · CSE 21, Final · Bank p.28 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Choose 2 consonants from 7 and 1 vowel from 3: C(7,2)·C(3,1)=21·3=63 selections. If the question means ordered three-character expressions, multiply by 3! to get 378.
Correction / source note
“Expressions” is ambiguous about order; both interpretations are shown.
Q25
Write the pseudocode for finding all possible nr . Your code should accept 2 parameters n and r. Given n = 4 and r = 3. Your code should output: 1,2,3 1,2,4 1,3,4 2,3,4
5 marks · CSE 19, TT2 · Bank p.28 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Use combination backtracking: choose the next integer strictly after the previous chosen one; when size r is reached, print.
Derivation / working
combine(start, chosen):
  if len(chosen)==r: print(chosen); return
  for x=start..n:
    choose x
    combine(x+1, chosen)
    unchoose x

For n=4,r=3: 123,124,134,234
Q26
You are tasked with designing an autonomous bombing drone. The drone starts from your army base A and must destroy all enemy bases (B, D, P ) before safely returning to base A. You must visit and destroy each enemy base exactly once before returning. Each path between bases contains a certain number of anti-drone defense systems. For example, the path from A to D has 5 defense systems, while the return path from D to A has 3. The drone gains more learning experience the more defense systems it encounters and survives. Your objective is to determine a route that allows the drone to destroy all enemy bases and return to the starting point A, while maximizing the total learning gained from encountering anti-drone systems. Your solution should allow constant-time checking and updating of visited bases. [12] Also: Explain a technique that allows constant-time checking and updating of visited enemy bases in the drone routing problem. Analyze the time complexity of your approach and evaluate whether the algorithm is feasible for N = 20 on a classical computer calculating the total number of operations. [8]
20 marks · CSE 23, TT2 · Bank p.29 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Maximum-learning Hamiltonian cycle is A→P→D→B→A with score 9+6+7+8 = 30.
Derivation / working
Routescore
A-B-D-P-A8+7+2+9=26
A-B-P-D-A8+1+6+3=18
A-D-B-P-A5+7+1+9=22
A-D-P-B-A5+2+1+8=16
A-P-B-D-A9+1+7+3=20
A-P-D-B-A9+6+7+8=30

Constant-time visited check/update: give each base a bit index. Test mask & (1<<v); visit with mask |= 1<<v; undo with XOR/restore old mask — all O(1) on a machine word for N≤word size.

Backtracking complexity: fixing start A leaves (N−1)! route permutations, with O(N) work per complete route if summing naively; incremental score gives Θ((N−1)!) node-order scale. For N=20, 19! = 121,645,100,408,832,000 permutations — not feasible on a classical machine.

Optional optimization: Held–Karp bitmask DP reduces to O(N²2ᴺ) time and O(N2ᴺ) memory (~4.19×10⁸ transition-scale operations for N=20), much better but still far heavier than simple polynomial algorithms.

Complete previous-year question map

Geometric Algorithms

All bank questions for this topic are represented. Question stays visible; answer stays collapsed. Clear duplicates are consolidated while every occurrence and alternate wording is retained.

PYQ19 bank questions
Q1
Define the term “convex hull.”
1 mark · CSE 21, Final · Bank p.29 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedThe convex hull of a point set is the smallest convex set/polygon containing every point; equivalently, the boundary formed by the outermost points.
Q2
Define the closest pair of points problem.
1 mark · CSE 23, Final · Bank p.29 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
The closest-pair problem asks for the two points in a set having minimum Euclidean distance.
Q3
Define convex hull and convex polygon. What is the time Complexity of finding Convex Hull using Graham Scan Algorithm?
3 marks · CSE 18, TT2 Set A · Bank p.29 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
A convex polygon has every interior angle ≤180° and contains every segment joining two of its points. The convex hull is the smallest convex polygon enclosing the set. Graham Scan runs in O(n log n), dominated by angular sorting.
Q4
Explain the difference between convex and concave polygons with suitable examples. Also define convex hull with an example.
3 marks · CSE 23, Final · Bank p.29 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedA convex polygon has every interior angle ≤180° and every segment between two points of the polygon stays inside it. A concave polygon has an indentation (some interior angle >180°). The convex hull is the smallest convex polygon enclosing the given points.
Q5
What are the conditions for two line segments to be Collinear?
2 marks · 2017-18 Akash · Bank p.30 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Two directed triples are collinear when orientation/cross product is zero. For segments AB and CD to lie on the same line, orient(A,B,C)=0 and orient(A,B,D)=0.
Q6
A line is drawn from (5, 5) to (10, 10). Then a second line is drawn from (10, 10) to (7, 25). Have the second line taken a counter-clockwise turn? Show the reason behind your answer.
2 marks · CSE 20, Final · Bank p.30 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Use cross product (P1−P0)×(P2−P1). For P0=(5,5), P1=(10,10), P2=(7,25): (5,5)×(−3,15)=5·15−5·(−3)=90>0, so it is a counter-clockwise/left turn under the standard coordinate convention.
Q7
How do you determine whether two consecutive line segments P0 P1 and P1 P2 turn left or right at point P1 ?
5 marks · CSE 20, Final · Bank p.30 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Compute the 2D cross product cross=(P1−P0)×(P2−P1). Its sign gives the turn; zero means collinear.
Derivation / working

Equivalent orientation formula: (x1−x0)(y2−y0) − (y1−y0)(x2−x0). With the usual Cartesian convention: cross>0 → counter-clockwise/left turn, cross<0 → clockwise/right turn, cross=0 → collinear.

Q8
Explain the time Complexity of Closest Pair of Points problem in efficient approach.
2 marks · 2017-18 Akash · Bank p.30 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Efficient closest pair: sort by x, divide in half, recursively solve both halves, then check a width-2δ strip sorted by y; only constant-many following points need comparison. T(n)=2T(n/2)+O(n)=O(n log n).
Q9
Describe the steps involved in detecting line segment intersections using the sweep line algorithm.
5 marks · CSE 21, Final · Bank p.30 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Sweep line: sort segment endpoints by x; maintain an ordered set of segments intersecting the current sweep line by y; on left endpoint insert and test neighbors, on right endpoint test the two neighbors that become adjacent then remove. Any neighbor intersection gives a hit; typical complexity O((n+k)log n).
Q10
Let the Line Segment 1 is built through point p1 (250, 142) and p2 (100, 67). Again Line Segment 2 is built through point p3 (-158, 96) and p4 (222, 128). Are these two Line Segments would be intersected or not? Show step by step.
5 marks · 2017-18 Akash · Bank p.30 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Orientation values are [-23700, 0, 4424, -19276]; the general intersection test gives intersect.
Derivation / working
For proper intersection, orientations of C,D about AB must have opposite signs and orientations of A,B about CD must have opposite signs; handle zero/collinear with on-segment checks.
Q11 / Q12
Write a function to check if the line segment between a pair of points A(x1 , y1 ) and B(x2 , y2 ) intersects with the line segment between the pair of points C(x3 , y3 ) and D(x4 , y4 ). struct point{ int x, y; point(int _x, int _y) {x = _x, y = _y;} } bool do_segments_intersect(point A, point B, point C, point D) { //... YOUR CODE HERE ... // YOU ARE FREE TO USE ANY PROGRAMMING LANGUAGE .... // EVEN PSUDOCODE IS ACCEPTABLE. }
Equivalent wording also in bank
Complete the do_segments_intersect function to check if the line segment between a pair of points A(x1 , y1 ) and B(x2 , y2 ) intersects with the line segment between another pair of points C(x3 , y3 ) and D(x4 , y4 ). struct point{ int x, y; point(int _x, int _y){x = _x, y = _y;} } bool do_segments_intersect(point A, point B, point C, point D) { ... YOUR CODE HERE ... }
5 marks · CSE 19, Final · Bank p.30 · Equivalent occurrence: 5 marks · CSE 19, TT2 · p.30 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Use four orientation tests plus bounding-box checks for collinear cases.
Derivation / working
orient(A,B,C) = sign((B-A) × (C-A))
o1=orient(A,B,C); o2=orient(A,B,D)
o3=orient(C,D,A); o4=orient(C,D,B)
if o1!=o2 and o3!=o4: return true
if oi==0 and corresponding point is on segment: return true
return false

onSegment checks that the collinear point lies within min/max x and y of the segment endpoints. Time O(1).

Q13
Given a polygon represented by the points A(2, 2), B(4, 2), C(4, 4) what would be the updated co-ordinates of the points A, B, C if you rotate the polygon 50 degrees counter clockwise with respect to the origin (0, 0).
5 marks · CSE 19, Final · Bank p.30 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Using a 50° counter-clockwise rotation about the origin: A′≈(−0.247,2.818), B′≈(1.039,4.350), C′≈(−0.493,5.635).
Derivation / working

Formula: x′=x cos50°−y sin50°, y′=x sin50°+y cos50°. Substitute A(2,2), B(4,2), C(4,4) separately.

Q14
Find the Convex Hull using Graham Scan Algorithm from the following points: (700, 70), (12, 543), (0, 50), (-200, 300), (-3400, 400), (-2600, 45), (700, 120), (-3400, 245), (-3100, 145).
8 marks · 2017-18 Akash · Bank p.31 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Convex hull in counter-clockwise order: (−3400,245) → (−3100,145) → (−2600,45) → (0,50) → (700,70) → (700,120) → (12,543) → (−3400,400).
Derivation / working

Graham Scan: choose the lowest-y pivot (−2600,45), sort remaining points by polar angle around the pivot, then maintain a stack and pop while the last two stack points plus the candidate do not make the required counter-clockwise turn. The final stack is the hull above. Sorting dominates: O(n log n).

Q15
Show all the steps to run Graham’s scan on the following points to form a convex-hull.
6 marks · CSE 16-17, Final; CSE 17, Final · Bank p.31 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Apply Graham Scan: select the lowest point p₀, sort p₁…p₁₂ by polar angle around p₀, then scan with a stack and pop on non-left turns until only the hull points remain.
Derivation / working

Write each stack after processing the next labeled point; collinear points on the same angle keep the farthest one. Complexity O(n log n).

Correction / source note
This PYQ supplies only a plotted figure with labels p0…p12, not numerical coordinates. The exact stack sequence depends on reading that plot; the original high-resolution bank page is embedded below for the drawing-based working.
Q16
Use the Graham Scan approach to find the Convex Hull of the given points. Show each step graphically. You don’t have to do the calculations to check anticlockwise orientation at each step. (10, 10), (27, 5), (15, 5), (23, 0), (35, 5), (30, 10), (20, 20).
5 marks · CSE 19, Final · Bank p.31 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
Hull = (10,10) → (15,5) → (23,0) → (35,5) → (20,20) (cyclic order).
Derivation / working

Choose pivot (23,0), sort by polar angle, and scan. Points (27,5) and (30,10) are interior to the resulting polygon and are removed by right-turn/non-left-turn pops. Final stack contains the five hull vertices above.

Q17
(a) Explain the difference between convex and concave polygons with suitable examples. Also define convex hull with an example. [3] (b) Apply either the Jarvis March (Gift Wrapping) algorithm or the Graham Scan algorithm to the set of points: P0(0,6), P1(1,4), P2(3,3), P3(2,1), P4(7,0), P5(8,2), P6(2,0), P7(0,0), P8(-2,-5) to determine the convex hull. You are not required to complete the full simulation. Instead, simulate the algorithm step-by-step until all key cases (e.g., left turn, right turn, collinear condition, point inclusion/exclusion) have been encountered at least once. [4]
10 marks · CSE 23, Final · Bank p.31 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
(a) A convex polygon has every interior angle ≤180° and every segment between two points of the polygon stays inside; a concave polygon has an inward dent/reflex angle. The convex hull is the smallest convex polygon containing all points. (b) For the given points, the full hull is P8(−2,−5), P4(7,0), P5(8,2), P0(0,6).
Derivation / working

During Graham/Jarvis simulation: a left turn keeps the candidate; a right turn causes the middle point to be popped/replaced; collinear candidates keep the farthest endpoint; interior points P1,P2,P3,P6,P7 are eventually excluded. These cases are enough for the requested partial simulation.

Q18
Write a step-by-step pseudocode for either the Jarvis March algorithm or the Graham Scan algorithm to compute the convex hull of a set of points.
3 marks · CSE 23, Final · Bank p.31 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedGraham Scan pseudocode: pivot=min(y,x); sort by polar angle; S=[pivot, first]; for each next p: while |S|≥2 and orientation(nextToTop(S),top(S),p) is not CCW: pop; push p; return S. Time O(n log n). Jarvis March alternative: repeatedly choose the most counter-clockwise next point; O(nh).
Q19
Find Closest Pair of Points from the following points in a O(n log n) approach and show step by step. (4, 3), (2, 3), (4, 5), (4.5, 5), (6, 6), (2, 4), (4.5, 3), (1, 4)
10 marks · 2017-18 Akash · Bank p.31 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Closest pair = (4,3) and (4.5,3); distance = 0.5.
Derivation / working

O(n log n) divide-and-conquer: sort points by x, split at median, recursively find left/right minima δ, build a strip of points within δ of the midline sorted by y, and compare each strip point with only the next constant number of y-neighbors. The cross-strip pair (4,3),(4.5,3) yields δ=0.5, which is the final minimum.

Complete previous-year question map

Number Theory

All bank questions for this topic are represented. Question stays visible; answer stays collapsed. Clear duplicates are consolidated while every occurrence and alternate wording is retained.

PYQ8 bank questions
Q1
Apply Euclid Algorithm to find the GCD of the pair of numbers 35 and 60.
2 marks · CSE 19, Final · Bank p.31 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement60=1·35+25; 35=1·25+10; 25=2·10+5; 10=2·5+0. Therefore gcd(35,60)=5.
Q2
Apply Extended Euclid algorithm on the pair of numbers 50 and 30 to find gcd(50, 30), x, and y such that 50x + 30y = gcd(50, 30).
5 marks · CSE 19, Final · Bank p.31 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
gcd(50,30)=10; one solution is x=−1, y=2.
Derivation / working
50 = 1·30 + 20
30 = 1·20 + 10
20 = 2·10 + 0

10 = 30 − 20
   = 30 − (50 − 30)
   = 2·30 − 50

Therefore 50(−1)+30(2)=10.

Q3
Who is (x, y) from 6 · x + 3 · y = 3 according to Extended Euclidean Algorithm?
2 marks · 2017-18 Akash · Bank p.31 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
One Extended-Euclid solution is x=0,y=1 because 6·0+3·1=3. The full integer family is x=t, y=1−2t.
Q4
When Modular Multiplicative Inverse is valid? And what is the range of Modular Multi- plicative Inverse of a order modulo m?
2 marks · 2017-18 Akash · Bank p.32 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplementa has a multiplicative inverse modulo m iff gcd(a,m)=1. The inverse is represented by one residue class modulo m; the standard least non-negative representative lies in 0…m−1 (and for a unit it is nonzero when m>1).
Q5
Find modular Multiplicative Inverse of 100 under modulo 817.
5 marks · 2017-18 Akash · Bank p.32 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
The modular inverse is 100⁻¹ mod 817 = 384.
Derivation / working
817 = 8·100 + 17
100 = 5·17 + 15
17 = 1·15 + 2
15 = 7·2 + 1

1 = 15 − 7·2
  = 15 − 7(17−15)
  = 8·15 − 7·17
  = 8(100−5·17) − 7·17
  = 8·100 − 47·17
  = 8·100 − 47(817−8·100)
  = 384·100 − 47·817

Hence 100·384 ≡ 1 (mod 817).

Q6
Apply Sieve of Eratosthenes to find all the prime numbers up to 20.
5 marks · CSE 19, Final · Bank p.32 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
Primes ≤20 are 2,3,5,7,11,13,17,19.
Derivation / working

Write 2…20. Starting with 2, cross out multiples 4,6,8,…; next uncrossed 3, cross out 9,12,15,18; next uncrossed 5 has 5²>20, so stop after primes p≤√20. Remaining uncrossed numbers are prime.

Q7
The following algorithm to check if the input number n is a prime number: For every integer 2 <= i <= sqrt(n), check if i divides n. If so declare n to be not a prime. If no such i exists, declare n to be a prime. Can we perform our task in polynomial time? You have to justify your answer properly.
5 marks · CSE 20, Final · Bank p.32 PYQ/curriculum supplement — no matching handwritten derivation identified.
Show answer
Verified supplement
Exam answer
The trial-division algorithm uses O(√n) arithmetic iterations. Measured against numeric value n, that is polynomial in n; however in complexity theory the input length is L=Θ(log n), so √n=2^{Θ(L)} and this particular algorithm is exponential in bit-length, not polynomial-time in the standard encoding model.
Q8
In an RSA public-key cryptosystem, a user selects two prime numbers, p = 11 and q = 13, to generate their key pair. The public exponent is chosen as e = 7. (a) Calculate n and φ(n). Using the Extended Euclidean Algorithm, find the private key d such that (e · d) ≡ 1 (mod φ(n)). Show all steps of the back-substitution. [5] (b) Suppose a sender wants to transmit the message M = 9. Show the encryption process to generate the ciphertext C. Briefly describe the decryption process at the receiver’s end. [3] (c) Discuss the computational difficulty of the Prime Factorization problem in the context of NP-Completeness. Why is it critical for the security of RSA that the choice of p and q be significantly large? [2]
10 marks · CSE 23, Final · Bank p.32 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linked
Exam answer
(a) n=143, φ(n)=120, private exponent d=103. (b) For M=9, ciphertext C=48; decryption recovers 9. (c) RSA relies on the classical difficulty of factoring large semiprimes.
Derivation / working
n = 11·13 = 143
φ(n) = (11−1)(13−1)=120

Extended Euclid:
120 = 17·7 + 1
1 = 120 − 17·7
  = 120 − (120−14·7)·7
  = 15·120 − 17·7
So 7(−17) ≡ 1 (mod 120) ⇒ d=−17 mod120=103.

Encryption:
C = 9^7 mod143 = 48
Decryption:
M = 48^103 mod143 = 9

Security requires p,q to be very large and randomly chosen so n is infeasible to factor with known classical algorithms.

Correction / source note
Integer factorization is not known to be NP-complete. Treating it as “NP-complete” would be an overclaim; RSA security is based on its observed/classical computational hardness, while Shor’s quantum algorithm can factor efficiently on a sufficiently large fault-tolerant quantum computer.
Complete previous-year question map

NP & Complexity

All bank questions for this topic are represented. Question stays visible; answer stays collapsed. Clear duplicates are consolidated while every occurrence and alternate wording is retained.

PYQ1 bank questions
Q1
Give example of an NP-hard problem. (Don’t just write the name of the problem. Write the objective briefly and clearly.)
2 marks · CSE 19, Final · Bank p.32 PYQ connection — directly matches supplied teacher/class-note material.
Show answer
Teacher-linkedExample: Traveling Salesman optimization problem — given weighted cities/edges, find a minimum-cost tour visiting every city exactly once and returning to the start. The optimization form is NP-hard; its decision version (“is there a tour of cost ≤K?”) is NP-complete.

Corrections & study notes

Visible correction notes included

  • TT1 recurrence T(n)=nT(n−1)+log n: notebook's O(n log n) conclusion is inconsistent; factorial-scale growth is flagged.
  • Quicksort: one partition is Θ(n), but balanced best/average full quicksort is Θ(n log n), not O(n).
  • Teacher slide: Θ is a tight asymptotic bound, not synonymous with average-case complexity.
  • Ford–Fulkerson vs Edmonds–Karp: path-choice/capacity-dependent method vs O(VE²) BFS specialization.
  • RSA: five-digit primes are toy-scale, not secure; raw textbook RSA is not a production encryption scheme.
  • Rabin–Karp: classroom rolling arithmetic is separated from the standard modular rolling hash.
  • P/NP: NP is not defined as “problems that take exponential time”; P vs NP remains unresolved.
  • Gift Wrapping: O(nh), therefore O(n²) worst case; collinear ties require farthest-point handling.

Diagram-dependent answers use readable question figures. Missing source information is explicitly flagged rather than invented.

No matching sections. Try another phrase or reset the material filter.

No matching material

Clear search or change the material filter.