시간 복잡도(Time Complexity)는 알고리즘의 실행 시간이 입력 크기에 대해 어떻게 증가하는지를 설명하는 개념이다. 일반적으로 더 빠른 알고리즘은 입력 크기가 증가함에 따라 더 적은 시간이 소요된다.
시간 복잡도는 빅오 표기법(Big O Notation)을 사용하여 표현된다. 몇 가지 흔히 사용되는 시간 복잡도의 예시는 다음과 같다.
void printFirstElement(int[] array) {
System.out.println(array[0]);
}
입력 크기에 관계없이 일정한 실행 시간을 가지는 경우이다. 예를 들어, 배열에서 특정 인덱스의 요소를 찾는 경우이다.
void binarySearch(int[] sortedArray, int target) {
int left = 0;
int right = sortedArray.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (sortedArray[mid] == target) {
// 원하는 값 찾음
return;
} else if (sortedArray[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
// 원하는 값이 없음
return;
}
입력 크기의 로그에 비례하는 실행 시간을 가지는 경우이다. 이진 탐색과 같이 입력을 절반씩 분할하여 탐색하는 알고리즘이 이에 해당한다.
void linearSearch(int[] array, int target) {
for (int i = 0; i < array.length; i++) {
if (array[i] == target) {
// 원하는 값 찾음
return;
}
}
// 원하는 값이 없음
return;
}
입력 크기에 비례하여 선형적으로 실행 시간이 증가하는 경우이다. 배열의 모든 요소를 한 번씩 확인하는 경우이다.
void bubbleSort(int[] array) {
int n = array.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
입력 크기의 제곱에 비례하는 실행 시간을 가지는 경우이다. 이중 반복문을 사용하여 모든 요소 쌍을 확인하는 경우이다.
int fibonacci(int n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
입력 크기에 대해 지수 함수에 비례하는 실행 시간을 가지는 경우이다. 조합, 순열과 같은 모든 가능한 경우의 수를 확인하는 경우이다.
재귀적인 구조나 모든 부분집합을 생성하는 경우에 해당한다.
void printPairs(int[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = i + 1; j < array.length; j++) {
System.out.println(array[i] + ", " + array[j]);
}
}
}
위의 코드는 주어진 배열의 각 요소들을 조합하여 쌍을 출력하는 함수이다. 이 코드의 시간 복잡도를 계산해보자.
외부 루프는 배열의 길이에 비례하는 O(n)의 시간이 소요된다. 내부 루프는 외부 루프의 반복 횟수에 따라 달라지며, 첫 번째 반복에서 (n-1)
번, 두 번째 반복에서 (n-2)
번, ..., 마지막 반복에서 1번의 연산이 수행된다. 이러한 내부 루프의 연산은 등차수열의 합과 관련이 있으며, 최종적으로 (n-1) + (n-2) + ... + 1 = n(n-1)/2
번의 연산이 수행된다.
따라서, 위의 예시 코드의 시간 복잡도는 O(n^2) 이다.
시간 복잡도를 고려할 때는 알고리즘의 효율성을 평가하고, 입력 크기에 따른 성능 예측과 알고리즘 선택에 도움을 준다. 더 작은 시간 복잡도를 가지는 알고리즘을 선택하면 보다 효율적인 프로그램을 개발할 수 있다.
트리의 균형 상태에 따라 접근, 검색, 추가, 삭제 모두 평균적으로 O(log n)이다. 하지만 트리가 한쪽으로 치우쳐진 경우 최악의 경우 O(n)의 시간이 소요될 수 있다.
일반적으로 우선순위 큐는 힙(heap) 자료구조를 기반으로 구현된다. 요소의 추가는 O(log n), 삭제는 O(log n), 최대 또는 최소값 조회는 O(1)이다.
키(key)
를 통해 값(value)
에 직접 접근 가능해시맵은 키와 값의 쌍을 저장하고 검색, 삽입, 삭제하는데 효율적이다. 그러나 해시맵은 내부적으로 배열을 사용하고 해시 충돌이 발생할 경우 선형 탐색이 필요할 수 있기 때문에, 최악의 경우에는 O(n)의 시간 복잡도를 가질 수도 있다.