← Back to list
Computing & Embedded
#알고리즘#복잡도#빅오#O-Notation#시간복잡도#134회
Last updated · 2026-07-05

Algorithm Complexity and O-Notation

1. Overview

A. Definition

Algorithm complexity is the amount of resources an algorithm consumes as a function of the input size (n). It is divided into time complexity, which measures the number of operations, and space complexity, which measures memory. O-Notation (Big-O) is a way of expressing the growth rate (asymptotic upper bound) as the input becomes sufficiently large.

The reason we measure complexity by the growth rate in the number of operations rather than the actual execution time (in seconds) is important. Execution time varies with CPU performance, language, and compiler, so it cannot determine the relative merits of an algorithm itself. But "if the input doubles, by how much does the number of operations grow?" is an intrinsic property of the algorithm that is independent of hardware. That is why O-Notation discards constant and lower-order terms and keeps only the growth rate of the most dominant term — for example, 3n²+5n+7 is written as O(n²) because the n² term dominates as n grows.

B. Types of Asymptotic Notation

There is not just O; there are three symbols that respectively express the upper bound, the lower bound, and the exact bound. In practice, since guaranteeing the worst case is important for design safety, O, which represents the upper bound, is the most widely used.

Notation Meaning Perspective
O (Big-O) Asymptotic upper bound Worst — performance guarantee
Ω (Big-Omega) Asymptotic lower bound Best
Θ (Big-Theta) Upper and lower bounds coincide Exact growth rate (average)

2. O-Notation Types and Operation Counts

The graph below shows how the operation count of each complexity diverges as the input size n grows. When n is small, the differences are negligible, but as n grows, anything O(n²) or worse diverges sharply and becomes practically infeasible. It is precisely this difference at large n that governs the choice of algorithm.

{
  "type": "line",
  "data": {
    "labels": ["1","2","4","8","16","32","64"],
    "datasets": [
      { "label": "O(1)", "data": [1,1,1,1,1,1,1], "borderColor": "#0e9f6e", "tension": 0.2 },
      { "label": "O(log n)", "data": [0,1,2,3,4,5,6], "borderColor": "#2f6fed", "tension": 0.2 },
      { "label": "O(n)", "data": [1,2,4,8,16,32,64], "borderColor": "#f59e0b", "tension": 0.2 },
      { "label": "O(n log n)", "data": [0,2,8,24,64,160,384], "borderColor": "#8b5cf6", "tension": 0.2 },
      { "label": "O(n^2)", "data": [1,4,16,64,256,1024,4096], "borderColor": "#e11d48", "tension": 0.2 }
    ]
  },
  "options": {
    "plugins": { "legend": { "position": "bottom" }, "title": { "display": true, "text": "Operation Count Growth vs. Input Size (n)" } },
    "scales": { "y": { "title": { "display": true, "text": "Number of Operations" } } }
  }
}

Each type arises from the operational structure of the algorithm. Once you understand why a given complexity results, you can gauge complexity just by looking at the code.

  • O(1) constant: A constant amount of work regardless of input size. This is the case when the location is computed in one step, as in hash lookups or array index access.
  • O(log n) logarithmic: A structure that halves the search range at each step. Binary search is the classic example; even for a million elements it finishes in about 20 steps.
  • O(n) linear: A structure that scans the input once. Sequential search and sum computation are examples.
  • O(n log n) linearithmic: A structure that splits the data (log n steps) and scans the whole (n) at each step. It is the lower bound of efficient sorting such as merge sort and quicksort.
  • O(n²) quadratic: A structure that compares all pairs with a nested loop. Bubble sort and insertion sort fall here, and they slow down sharply as n grows.
  • O(2ⁿ) exponential: A structure that branches into twice as many cases at each step. Non-memoized recursive Fibonacci and subset enumeration are examples.
  • O(n!) factorial: A structure that tries every permutation. Brute-force search of the Traveling Salesman Problem (TSP) is the representative case, and it becomes uncomputable even at n=20.
Type Name Structural Principle Example
O(1) Constant Direct access Hash lookup, array index
O(log n) Logarithmic Halving the range Binary search
O(n) Linear Single traversal Sequential search
O(n log n) Linearithmic Divide + traverse Merge sort, quicksort
O(n²) Quadratic Nested loop (all pairs) Bubble sort, insertion sort
O(2ⁿ) Exponential Branching by a factor of 2 Subsets, recursive Fibonacci
O(n!) Factorial All permutations TSP brute force

Growth rate: O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ) < O(n!)

3. Case Analysis

Even the same algorithm performs differently depending on the state of the input, so we analyze the best, average, and worst cases separately. For example, quicksort averages O(n log n) when the pivot is close to the median each time, but on an already-sorted array with a poorly chosen pivot the partitioning becomes lopsided, resulting in worst-case O(n²). In design, the worst case (O) should generally serve as the baseline so that performance is guaranteed in production service.

Case Notation Meaning
Best Ω Performance on the fastest input
Average Θ Expected performance
Worst O Guaranteed upper bound — the design baseline

4. Considerations and Implications

  • At large n, complexity dominates performance: For small data the constant-factor differences are large, so O(n²) can even be faster than O(n log n), but at large scale the asymptotic complexity is decisive. Choosing an algorithm suited to the data scale is key.
  • Time-space trade-off: Complexity is traded between time and space. For example, caching and memoization use more memory (space) to reduce repeated computation (time) — memoizing recursive Fibonacci reduces O(2ⁿ) to O(n), a representative case.
  • Complementing the limits of asymptotic analysis: Constant terms, hardware, cache locality, and constant multipliers are ignored, so during actual tuning you should complement it with profiling and real measurements.
  • Connections and outlook: Whether a problem can be solved in polynomial time (P) is a core topic of computational complexity theory such as P vs. NP, and NP-hard problems that are O(n!) like TSP are solved practically with approximation, heuristics, and dynamic programming. Complexity analysis is the basic yardstick for algorithm selection and optimization.

In one line: O-Notation expresses an algorithm's worst-case growth rate (asymptotic upper bound) with respect to input size in a hardware-independent way; performance degrades sharply in the order O(1)→O(log n)→O(n)→O(n log n)→O(n²)→O(2ⁿ)→O(n!), and algorithms are chosen considering the time-space trade-off and dominance at large n.