CSE330Semester 5 • 3 CreditsElective Subjects

Competitive Coding Approaches-Techniques

This subject develops competitive coding skills through algorithm complexity analysis, primality testing, recursion, backtracking, dynamic programming, and efficient sorting algorithms. Students apply these techniques to solve computational and optimization problems.

6 Units • Notes • Model Answers • MCQs
Select Course Unit (6 Units)
Active: Unit 1
Unit 1:Unit 1: Behaviour Analysis
7 min read•High-Yield Unit Guide

Unit 1: Behaviour Analysis

Unit 1 Exam Focus & Core Takeaways

Review the core definitions, standard architectures, and syntax patterns below. Pay special attention to sections highlighted with bullet points for your mid-term and end-term subjective questions.

Unit 1: Behaviour Analysis of Algorithms

1. Architectural Foundations of Algorithmic Behaviour

The Random Access Machine (RAM) Model

In computational complexity and competitive coding, algorithmic performance is analyzed against an idealized hardware model termed the Random Access Machine (RAM):

  • Memory consists of an unbounded array of uniform memory cells, each capable of holding an integer or pointer of standard word size (ww bits, typically 32 or 64 bits).
  • Basic scalar operations execute in strictly O(1)O(1) constant time:
    • Arithmetic operations: addition, subtraction, multiplication, integer division, modulo (+, -, *, /, %).
    • Bitwise operations: shifts, bitwise AND, OR, XOR (<<, >>, &, |, ^, ~).
    • Pointer dereferencing, indexing, and assignment.
  • Operations on arbitrary-precision numbers (strings or big-integers with LL digits) are not O(1)O(1); they scale as O(L)O(L) or O(Llog⁡L)O(L \log L).

Mathematical Limit Hierarchy

The relative asymptotic growth rate of two complexity functions f(n)f(n) and g(n)g(n) as input size n→∞n \to \infty is rigorously evaluated using L'Hôpital's rule and mathematical limits:
L=lim⁡n→∞f(n)g(n)L = \lim_{n \to \infty} \frac{f(n)}{g(n)}

  1. L=0L = 0: f(n)f(n) grows strictly slower than g(n)g(n) (f(n)∈o(g(n))f(n) \in o(g(n))).
  2. 0<L<∞0 < L < \infty: f(n)f(n) and g(n)g(n) have identical asymptotic rates of growth (f(n)∈Θ(g(n))f(n) \in \Theta(g(n))).
  3. L=∞L = \infty: f(n)f(n) grows strictly faster than g(n)g(n) (f(n)∈ω(g(n))f(n) \in \omega(g(n))).

2. Asymptotic Notations: Formal Definitions and Set Theory

Asymptotic Upper Bound: Big-O (OO)

f(n)∈O(g(n))f(n) \in O(g(n)) if and only if there exist positive constants c>0c > 0 and n0≥1n_0 \ge 1 such that:
0≤f(n)≤c⋅g(n)for all n≥n00 \le f(n) \le c \cdot g(n) \quad \text{for all } n \ge n_0
Big-O provides a worst-case asymptotic ceiling on resource growth.

Asymptotic Lower Bound: Big-Omega (Ω\Omega)

f(n)∈Ω(g(n))f(n) \in \Omega(g(n)) if and only if there exist positive constants c>0c > 0 and n0≥1n_0 \ge 1 such that:
0≤c⋅g(n)≤f(n)for all n≥n00 \le c \cdot g(n) \le f(n) \quad \text{for all } n \ge n_0
Big-Omega represents an asymptotic floor beneath which the function cannot drop for large nn.

Asymptotically Tight Bound: Big-Theta (Θ\Theta)

f(n)∈Θ(g(n))f(n) \in \Theta(g(n)) if and only if f(n)∈O(g(n))f(n) \in O(g(n)) and f(n)∈Ω(g(n))f(n) \in \Omega(g(n)). Formally:
0≤c1⋅g(n)≤f(n)≤c2⋅g(n)for all n≥n00 \le c_1 \cdot g(n) \le f(n) \le c_2 \cdot g(n) \quad \text{for all } n \ge n_0

Strict Asymptotic Bounds: Little-o (oo) and Little-omega (ω\omega)

  • Little-o (oo): f(n)∈o(g(n))f(n) \in o(g(n)) means for every positive constant c>0c > 0, there exists n0n_0 such that 0≤f(n)<c⋅g(n)0 \le f(n) < c \cdot g(n) for all n≥n0n \ge n_0. Equivalently, lim⁡n→∞f(n)/g(n)=0\lim_{n \to \infty} f(n)/g(n) = 0.
  • Little-omega (ω\omega): f(n)∈ω(g(n))f(n) \in \omega(g(n)) means lim⁡n→∞f(n)/g(n)=∞\lim_{n \to \infty} f(n)/g(n) = \infty.

3. The Canonical Complexity Class Hierarchy

Ranked from slowest growing (most efficient) to fastest exploding:
O(1)<O(log⁡log⁡n)<O(log⁡n)<O(n)<O(n)<O(nlog⁡n)<O(n2)<O(n3)<O(2n)<O(n!)O(1) < O(\log \log n) < O(\log n) < O(\sqrt{n}) < O(n) < O(n \log n) < O(n^2) < O(n^3) < O(2^n) < O(n!)

  • Constant O(1)O(1): Hash table lookup, stack push/pop, array indexing.
  • Double Logarithmic O(log⁡log⁡n)O(\log \log n): Interpolation search, van Emde Boas trees.
  • Logarithmic O(log⁡n)O(\log n): Binary search, balanced BST operations, binary exponentiation.
  • Square Root O(n)O(\sqrt{n}): Trial division primality testing, Mo's algorithm, square root decomposition.
  • Linear O(n)O(n): Two pointers, Kadane's algorithm, prefix sum construction, linear sieve.
  • Linearithmic O(nlog⁡n)O(n \log n): Merge sort, quicksort (average), segment tree construction.
  • Polynomial O(n2),O(n3)O(n^2), O(n^3): Nested grid iterations, Floyd-Warshall, matrix multiplication.
  • Exponential & Factorial O(2n),O(n!)O(2^n), O(n!): Subsets, bitmask DP, permutations, exhaustive brute force.

4. Analysis Paradigms: Worst, Average, Best, and Amortized

Input-Dependent Behaviors

  1. Worst-Case Analysis Tworst(n)T_{\text{worst}}(n): The maximum number of basic operations taken over all possible inputs of size nn. This is the standard metric in competitive programming because contest judges deliberately submit adversarial test cases.
  2. Average-Case Analysis Tavg(n)T_{\text{avg}}(n): The expected value E[T(I)]\mathbb{E}[T(I)] over a probabilistic distribution of inputs. For example, randomized Quicksort runs in O(nlog⁡n)O(n \log n) expected time.
  3. Best-Case Analysis Tbest(n)T_{\text{best}}(n): The minimum number of steps taken over any input of size nn (e.g., O(n)O(n) for insertion sort on an already sorted array).

Amortized Analysis Techniques

Amortized analysis averages the running time of a sequence of operations over time, guaranteeing an average cost per operation even if individual steps are occasionally expensive:

  1. Aggregate Method: Show that a sequence of kk operations takes T(k)T(k) total time in the worst case; the amortized cost per operation is T(k)/kT(k) / k.
  2. Accounting (Banker's) Method: Charge artificial credits (amortized cost) to cheap operations. These stored credits pay for subsequent expensive operations.
  3. Potential Method (Physicist's Method): Define a potential function Φ(D)\Phi(D) mapping the data structure state to a non-negative real number (Φ(D0)=0,Φ(Di)≥0\Phi(D_0) = 0, \Phi(D_i) \ge 0). The amortized cost of the ii-th operation is:
    c^i=ci+Φ(Di)−Φ(Di−1)\hat{c}_i = c_i + \Phi(D_i) - \Phi(D_{i-1})
    Summing over kk operations: ∑i=1kc^i=∑i=1kci+Φ(Dk)−Φ(D0)≥∑i=1kci\sum_{i=1}^k \hat{c}_i = \sum_{i=1}^k c_i + \Phi(D_k) - \Phi(D_0) \ge \sum_{i=1}^k c_i.

5. Practical Competitive Programming Budgets and Scaling Rules

The 10810^8 Operations Per Second Benchmark

Standard online judge CPU cores (Codeforces, AtCoder, CSES, LeetCode) execute roughly 10810^8 simple integer/bitwise operations per second:

  • Time Limit = 1.01.0 second   ⟹  ≈108\implies \approx 10^8 operations.
  • Time Limit = 2.02.0 seconds   ⟹  ≈2×108\implies \approx 2 \times 10^8 operations.

Input Constraint-to-Algorithm Mapping Table

Constraint on nnTarget Complexity BoundTypical Algorithmic Approaches
n≤10n \le 10O(n!) or O(n2⋅2n)O(n!) \text{ or } O(n^2 \cdot 2^n)Permutations, Travelling Salesperson (TSP) via Bitmask DP
n≤20n \le 20O(2n) or O(n⋅2n)O(2^n) \text{ or } O(n \cdot 2^n)Subset generation, meet-in-the-middle, bitmask dynamic programming
n≤50n \le 50O(n4)O(n^4)4-nested loops, Gaussian elimination, dynamic programming on quadruplets
n≤400n \le 400O(n3)O(n^3)Floyd-Warshall all-pairs shortest paths, cubic interval DP, matrix multiplication
n≤2000n \le 2000O(n2)O(n^2)All-pairs comparison, insertion sort, quadratic DP, grid BFS
n≤105n \le 10^5O(nlog⁡n)O(n \log n)Sorting, Segment Trees, Fenwick Trees (BIT), divide-and-conquer
n≤106n \le 10^6O(n)O(n)Two pointers, monotonic stacks, prefix sums, linear sieves
n≤109n \le 10^9O(n) or O(log⁡n)O(\sqrt{n}) \text{ or } O(\log n)Sieve to n\sqrt{n}, binary search, Euclidean GCD, matrix exponentiation
n≤1018n \le 10^{18}O(log⁡n) or O(1)O(\log n) \text{ or } O(1)Fast exponentiation, Miller-Rabin test, closed-form formulas

Memory Budgeting and Stack Overflow Prevention

  • Memory Limit: Typically 256256 MB.
    • 1 MB=1024×1024 bytes≈106 bytes1 \text{ MB} = 1024 \times 1024 \text{ bytes} \approx 10^6 \text{ bytes}.
    • A 32-bit int takes 4 bytes. An array of 10710^7 integers takes 4040 MB.
    • An array of size 10810^8 integers consumes 400400 MB, triggering Memory Limit Exceeded (MLE).
  • Call Stack Limit: Linux platforms allocate an 8 MB default stack. Recursive depth exceeding 10510^5 nested frames triggers Runtime Error / Stack Overflow (SIGSEGV). Deep recursions (DFS on trees/graphs) must be converted to iterative stacks or use custom stack pragmas.