빅 오 표기법(Big O Notation)
알고리즘의 효율성을 평가하고, 프로그램의 성능을 예측하는 데 사용되는 개념이자 알고리즘이 입력 크기에 따라 어떻게 증가하는지를 간결하게 표현하는 방법
O(f(n))
으로 표기
O
는 빅 오(O) 기호를 의미 f(n)
은 알고리즘의 입력 크기 n
에 따라 실행 시간 또는 메모리 공간 사용량이 어떻게 변하는지를 설명하는 함수O(n)
은 입력 크기 n
에 비례하여 증가하는 시간 복잡도를 의미빅 오 표기법을 쓰는 이유
function changeFirstElement(arr, newValue) {
arr[0] = newValue; // 배열의 첫 번째 요소에 새로운 값을 할당
}
function binarySearch(arr, target) {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) {
return mid; // 원하는 값 찾음
} else if (arr[mid] < target) {
left = mid + 1; // 중간보다 오른쪽 부분 배열 검색
} else {
right = mid - 1; // 중간보다 왼쪽 부분 배열 검색
}
}
return -1; // 원하는 값이 배열에 없음
}
log n
개념 정리n
은 입력 값이며, 로그의 밑은 일반적으로 2 또는 10이 사용됨log₂(n)
: 주어진 수 "n"을 2로 나누어야 1 이하의 값이 되는 횟수를 의미function findMax(arr) {
let max = arr[0]; // 배열의 첫 번째 요소를 최댓값으로 초기화
for (let i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i]; // 더 큰 값이 나타나면 최댓값을 갱신
}
}
return max; // 최댓값 반환
}
n log n
번의 비교와 병합 작업이 수행log₂(n)
번의 비교 작업 + 각 배열을 정렬하고 병합할 때 n번의 비교 작업// 병합 정렬 함수
function mergeSort(arr) {
// 입력 배열이 이미 정렬되어 있거나 길이가 1 이하인 경우, 그대로 반환
if (arr.length <= 1) {
return arr;
}
// 입력 배열을 반으로 나누기 위한 중간 인덱스 계산
const middle = Math.floor(arr.length / 2);
// 배열을 반으로 나누어 좌측과 우측 부분 배열로 분리
const left = arr.slice(0, middle);
const right = arr.slice(middle);
// 좌측과 우측 부분 배열을 재귀적으로 정렬하고 병합
return merge(mergeSort(left), mergeSort(right));
}
// 두 부분 배열을 병합하는 함수
function merge(left, right) {
let result = [];
let leftIndex = 0;
let rightIndex = 0;
// 좌측과 우측 부분 배열을 비교하며 병합
while (leftIndex < left.length && rightIndex < right.length) {
if (left[leftIndex] < right[rightIndex]) {
result.push(left[leftIndex]);
leftIndex++;
} else {
result.push(right[rightIndex]);
rightIndex++;
}
}
// 남은 요소들을 병합
return result.concat(left.slice(leftIndex), right.slice(rightIndex));
}
function bubbleSort(arr) {
const n = arr.length;
for (let i = 0; i < n - 1; i++) {
for (let j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 두 요소를 교환
const temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
O(1) < O(log n) < O(n) < O(n log n) < O(n^2)
function solution(my_string, n) {
return my_string.split('').map((str)=> str.repeat(n)).join('')
}
split('')
: 주어진 문자열을 배열로 분할
=> O(n)
n은 입력 문자열 my_string의 길이
map((str) => str.repeat(n))
: 배열의 각 문자를 n번 반복하여 새로운 배열을 생성
=> O(n)
n은 입력 문자열 my_string의 길이
join('')
: 배열의 모든 요소를 하나의 문자열로 결합
=> O(n)
n은 결과 문자열의 길이
이 함수의 전체 복잡도
입력 크기가 충분히 커지면 상수항은 큰 영향을 미치지 않는다.
O(3n)
과 O(n)
의 차이는 무시 된다.상수항을 포함하면 복잡한 수식으로 표현되어 알고리즘의 성능을 비교하기 어렵다.
5n^2 + 3n + 2
1000n + 5000
=> 주로 주요한 영향을 미치는 부분인 입력 크기에 대한 가장 큰 항만을 고려하고, 상수나 낮은 차수의 항은 무시하여 단순하게 표현한다.