매일 아침, 일어나서 컴퓨터에 앉자마자 반드시 진행 중인 루틴이 하나 있다. 코딩테스트를 적어도 한 문제 진행하는 것이다.
유명한 코딩테스트 사이트인 백준, 프로그래머스, 리트코드 중 프로그래머스에서 하루에 한 문제 이상씩 풀어보며 코딩테스트에 대한 감을 유지하는 중이다.

아직 너무 어려운 코딩테스트 풀이까지는 진입하지 않았다. 전체 문제를 정답률 높은 문제 순으로 필터링하여 차근차근 풀고 있다.
그러다보니 어느덧, 그렇게 많은 숫자는 아닐지 몰라도 432 문제를 풀었다. 매일마다 늘어나는 순위와 해결한 문제를 보며, 작은 성취감과 자존감을 채워나가고 있다.
코딩테스트를 진행하며, 시간 복잡도 문제로 골머리를 많이 앓고 있다. 내가 생각한 시간 복잡도와 내 풀이의 시간 복잡도가 다를 때도 가끔 있고, 시간 복잡도를 줄이지 못해 런타임 에러가 터지는 경우도 왕왕 있다.
이러한 문제들을 잘 해결하기 위해서는 당연히 시간 복잡도에 대해 잘 알고 있어야 한다. 그렇기에 시간 복잡도에 대해서 간략하게나마 정리해보았다.
(10^8) , 즉 1억 번의 연산을 처리할 수 있다고 가정n 의 크기와 상관없이, 실행 시간은 항상 일정n - 제한 없음public int sum(int x, int y) {
return x + y;
}
x 와 y 의 값에 상관 없이 항상 한 번의 계산으로 실행이 종료됨O(1)n 의 크기에 따라 실행 시간이 로그 증가n - 10^18 이상도 가능, 매우 넉넉한 조건public int binarySearch(int[] arr, int key) {
int left = 0, right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == key) {
return mid;
} else if (arr[mid] <= key) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
while (left <= right) 일 때, 배열을 반으로 나누는 작업 반복n 을 반으로 나누는 횟수는 logn 에 비례O(logn)n 의 값과 비례하여 실행 시간 증가n <= 100,000,000 ~ 1,000,000,000public int sumArr(int[] arr) {
return Arrays.stream(arr).sum();
}
arr 의 크기 n 을 모두 한 번씩 순회하며 값을 더함O(n)n 값 * 로그 증가) 만큼의 실행 시간n <= 1,000,000 ~ 10,000,000public void mergeSort(int[] arr) {
if (arr.length < 2) {
return;
}
int mid = arr.length / 2;
int[] left = Arrays.copyOfRange(arr, 0, mid);
int[] right = Arrays.copyOfRange(arr, mid, arr.length);
mergeSort(left);
mergeSort(right);
merge(arr, left, right);
}
public void merge(int[] arr, int left, int right) {
// 두 배열을 합치는 알고리즘
// 생략
}
mergeSort(left) 와 mergeSort(right) 에서 배열을 반으로 나누므로, logn 번 반복O(n)O(nlogn)n 일 때, n * n 만큼 실행n <= 1,000 ~ 10,000public void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
for 루프로 인해 n * n 번 실행O(n^2)n 일 때, n * n * n 만큼 실행n <= 100 ~ 300public void flodWarshall(int[][] graph) {
int n = graph.length;
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
graph[i][j] = Math.min(graph[i][j], graph[i][k] + graph[k][j]);
}
}
}
}
for 루프로 인해 n * n * n 번 실행O(n^3)n 일 때, 2 의 n 제곱 만큼 실행n <= 약 20public class Solution {
List<Integer> list;
public static void main(String[] args) {
list = new ArrayList();
subsetSum(new int[]{1, 2, 3, 4, 5}, 0, 0);
}
public void subsetSum(int[] arr, int sum, int idx) {
if (idx == arr.length) {
list.add(sum);
return;
}
subsetSum(arr, sum + arr[idx], idx + 1);
subsetSum(arr, sum, idx + 1);
}
}
arr 의 요소를 더할 경우와 더하지 않을 경우 총 2가지 실행subsetSum 호출시마다 2가지 경우의 수가 존재함O(2^n)n! 만큼 실행n <= 약 10
public class Solution {
int result;
boolean[] visited;
public int solution(int k, int[][] dungeons) {
result = Integer.MIN_VALUE;
visited = new boolean[dungeons.length];
dfs(k, dungeons, 0, 0);
return result;
}
public void dfs(int k, int[][] dungeons, int depth, int count) {
if (depth == dungeons.length) {
result = Math.max(result, count);
return;
}
for (int i = 0; i < dungeons.length; i++) {
if (!visited[i]) {
if (k >= dungeons[i][0]) {
k -= dungeons[i][1];
visited[i] = true;
dfs(k, dungeons, depth + 1, count + 1);
visited[i] = false;
k += dungeons[i][1];
}
}
}
result = Math.max(result, count);
}
}
n 일 경우, 재귀 호출 시마다 n - 1 개의 경우의 수 고려n - 1 씩 줄여가며 반복O(n!)| 시간 복잡도 | 최대 ( n ) (1초 기준) | 설명 |
|---|---|---|
| O(1) | 제한 없음 | 상수 시간 연산은 항상 가능 |
| O(log n) | 매우 큼 ( 10^18 ) | 로그 시간은 큰 입력에서도 가능 |
| O(n) | 약 ( 10^8 ) | 선형 시간은 입력이 매우 클 때도 가능 |
| O(n log n) | 약 ( 10^6 ) ~ ( 10^7 ) | 병합 정렬, 힙 정렬 등에서 자주 사용 |
| O(n^2) | 약 ( 10^3 ) ~ ( 10^4 ) | 이중 루프 등에서 사용 가능 |
| O(n^3) | 약 ( 100 ) ~ ( 300 ) | 플로이드-워셜 등 |
| O(2^n) | 약 ( 20 ) | 부분집합 탐색, 완전 탐색에서 사용 |
| O(n!) | 약 ( 10 ) | 순열 생성, 최적화 문제에서 사용 |
참고) OpenAI. (2024).ChatGPT(4o)[Large language model].https://chatgpt.com/