시간 복잡도
는 문제를 해결하기 위한 연산 횟수를 말하며, 일반적으로 1초에 1억번 연산
하는 것을 기준으로 생각합니다.빅-오메가
: 최선일 때의 연산 횟수를 나타낸 표기법빅-세타
: 보통일 때의 연산 횟수를 나타낸 표기법빅-오
: 최악일 때의 연산 횟수를 나타낸 표기법예시 문제를 풀어보며 시간복잡도 활용방법에 대해 알아보겠습니다.
Q. 문자열의 알파벳 구성을 파악하는 코드의 시간복잡도는?
for (int i = 0; i <str.length(); i++){
int alphabetIndex = str.charAt(i) - 'A'; // 아스키코드 구하기
count[alphabetIndex]++;
}
+1
+(L+1)
+L
+(2*L)
총 연산의 횟수는 4*L +2
로 입력된 문자열의 길이에 4배만큼 비례합니다.
표기법으로 나타내면 O(4*L+2)
인데 우리는 데이터의 크기의 비례한 연산 횟수만을 알고싶기 때문에 모든 상수는 전부 생략해줍니다.
따라서 이 문제의 시간복잡도는 O(L)
입니다. 이 표기법의 의미는 입력된 데이터의 길이에 비례하는 수행시간을 가진 알고리즘 이라고 볼 수 있습니다.
알고리즘 수행이 입력된 데이터(N)에 비례해 선형적으로 증가합니다. 반복문을 기반으로 배열의 모든 원소를 순차탐색하는 형태를 선형 탐색
이라고 하며, 선형 탐색의 시간복잡도가 O(N)
으로 자주 나타납니다.
이제 선형탐색안에서 다른형태의 탐색이 진행되는 예시문제를 보겠습니다.
Q. 이중반복문의 시간복잡도는?
long sum = 0 ;
for(int i = 0; i < N; i++){
for(int j = 0; j < M; j++){
sum += (long)a[i] * b[j]; // 이연산은 M*N번 반복된다.
}
}
전체 시간복잡도는 O(M*N)
이 됩니다.
알고리즘 | 시간 복잡도 |
---|---|
사칙연산 | O(1) |
선형탐색 | O(N) |
정렬 | O(NlogN) |
이진 탐색 | O(logN) |
조합 | O(2^N) |
순열 | O(N!) |
정렬된 배열에서 특정 원소의 위치를 찾는 문제를 생각해볼때, 배열의 모든 원소를 순회한다면 O(N) 시간 복잡도가 소요됩니다. 하지만 배열을 정렬한다면 이진탐색을 적용하여 O(logN)으로 줄일 수 있습니다.
따라서 문제 조건에 맞는 적절한 자료구조와 알고리즘을 이용하는 것이 시간 복잡도를 줄일 수 있는 핵심입니다.
문제를 보고 효율적인 알고리즘을 바로 떠올리기 어렵다면 문제에서 주어진 입력 조건을 이용하여 시간 복잡도를 유추해 볼 수도 있습니다. 아래 표는 제한시간 1초일때 입력조건에 맞는 유추 가능한 알고리즘입니다.
입력 데이터(N) | 시간 복잡도 | 알고리즘 |
---|---|---|
10 | O(N!) | 순열 |
20 | O(2^N) | 조합 |
1,000 ~ | O(N^3), O(N^3logN) | 완전 탐색, 이진 탐색 |
10,000 ~ | O(NlogN) | 정렬, 이진 탐색 |
무조건 해당 입력 조건에 해당 알고리즘을 사용하라는 것은 아니며, 어디까지나 힌트로 사용하면 좋습니다. 항상 시간 복잡도를 따지고 효율성이 검증되면 그때 문제를 푸는 습관을 들이는게 좋습니다.