본문 바로가기

Programming

시간 복잡도

시간 복잡도란 프로그램의 입력 값과 연산 수행 시간의 상관관계를 나타내는 척도이다. 다시 말해 입력 크기 n에 따른 알고리즘의 연산 횟수를 수학적으로 표현한 것이다.

 


점근적 표기법

 

n 이 커질 때 알고리즘 실행 시간이 어떻게 늘어나는지를 그 함수의 증가율이라 한다. 이때 상수항과 상수배(계수), 하위 차수 항을 무시하고 증가율만 비교하면 핵심에 집중할 수 있는데, 이를 점근적 표기법(Asymptotic notation) 이라 부른다. 다시 말해 점근적이라는 의미는 가장 큰 영향을 주는 항만 계산한다는 의미이다.

점근적 표기법은 다음과 같이 3가지로 나뉜다.

 

  • 최상의 경우 : 오메가 표기법 (Big-Ω Notation)      [바닥선 : “적어도 이만큼은 걸린다.”]
  • 평균의 경우 : 세타 표기법 (Big-θ Notation)          [정확선 : “대략 이만큼이다.”]
  • 최악의 경우 : 빅오 표기법 (Big-O Notation)         [천장선 : “이보다 더 느리진 않다.”]

프로그래밍에서는 대부분 빅오 표기법을 시간복잡도 표기법으로 사용한다.

 


빅오 표기법 (Big-O Notation)

 

Big-O는 입력 크기 n 이 커질 때 알고리즘이 소모하는 자원(시간·메모리 등)의 상한(upper bound) 을 나타내는 점근적 표기다. 이러한 Big-O는 분석을 단순화하기 위해 상수항과 하위 차수 항을 무시하고 증가율만 본다.

아래 표는 자주 쓰는 Big-O 등급을 정리한 것이다.

 

O(1) : 상수 시간

입력 크기와 상관없이 연산이 한 번에 끝나는 경우

int getFirst(int[] a) {
    return a[0];
}

 

 

O(log N) : 로그 시간

연산을 한 번 할 때마다 절반(혹은 일정 비율) 로 남은 연산의 양을 줄이는 경우

int binarySearch(int[] a, int x) { 
    int l=0, r=a.length-1;
    while (l<=r) {
        int m = l + (r-l)/2;
        if (a[m]==x) return m;
        if (a[m]<x) l=m+1; else r=m-1;
    }
    return -1;
}

 

 

O(N) : 선형 시간

입력값 크기 만큼 연산을 해야 하는 경우

int max(int[] a) {
    int m = Integer.MIN_VALUE;
    for (int v : a) m = Math.max(m, v);
    return m;
}

 

 

O(N log N) : 선형로그 시간

log N 단계를 N번 반복하는 경우 → N × log N

// 정렬된 배열 (검색 대상)
int[] a = {1, 3, 5, 7, 9, 11, 13, 15};

// 탐색할 값들 (queries)
int[] queries = {3, 6, 11, 14};

for (int q : queries) {
int idx = Arrays.binarySearch(a, q);       // Q번 × log N = O(Q log N)
        System.out.println("query=" + q + ", result=" + idx);
}

 

 

O(N²) : 제곱 시간

모든 쌍(이중 루프) 또는 한 쪽 길이에 비례해 다른 쪽을 반복하는 경우

int countPairsLess(int[] a, int t) { // 모든 쌍 비교
    int cnt=0;
    for (int i=0;i<a.length;i++)
        for (int j=i+1;j<a.length;j++)
            if (a[i]+a[j] < t) cnt++;
    return cnt;                      // O(N^2)
}

 

 

O(2^N), O(N!) : 지수/팩토리얼 시간

부분집합/순열을 전부 만들어 보는 경우 → N이 조금만 커져도 폭발

void subsets(int[] a, int idx, List<Integer> cur) {
    if (idx==a.length) { /* 사용 */ return; }
    subsets(a, idx+1, cur);          // 포함 X
    cur.add(a[idx]);
    subsets(a, idx+1, cur);          // 포함 O
    cur.remove(cur.size()-1);         // 총 2^N 경로
}

 


자료 구조별 시간 복잡도

 

아래 표는 일반적인 평균/전형적 가정을 기준으로 한 요약이다.

 

Data Structures Average  Worst 
Search Insert  Delete  Search Insert  Delete 
Array O(n) O(n) O(n) O(n) O(n) O(n)
Sorted Array O(log n) O(n) O(n) O(log n) O(n) O(n)
Linked List O(n) O(1) O(1) O(n) O(1) O(1)
Doubly Linked List O(n) O(1) O(1) O(n) O(1) O(1)
Stack O(n) O(1) O(1) O(n) O(1) O(1)
Hash table O(1) O(1) O(1) O(n) O(n) O(n)
Binary Search Tree (일반 BST) O(log n) O(log n) O(log n) O(n) O(n) O(n)
B-Tree O(log n) O(log n) O(log n) O(log n) O(log n) O(log n)
Red-Black Tree O(log n) O(log n) O(log n) O(log n) O(log n) O(log n)
AVL Tree O(log n) O(log n) O(log n) O(log n) O(log n) O(log n)

출처 : 
https://blog.chulgil.me/algorithm/
https://ssdragon.tistory.com/100
https://www.baeldung.com/java-collections-complexity?utm_source=chatgpt.com
https://www.cl.cam.ac.uk/teaching/1819/OOProg/complexity.pdf