by 꽈배기
시간과 공간의 복잡도를 다룬다 예를 들어 어떠한 알고리즘이 존재할 때, 추가적인 메모리 용량을 사용하고 더 빠른 속도를 낼 수 있거나 반대로 추가 메모리 사용 없이 문제를 해결하는 대신 느린 경우가 존재할 수 있다.
시간복잡도의 경우 Quadratic, Linear, Logarithmic ,Constant로 분류할 수 있다. 각각 n^2^, 선형 (n), Logn, 상수로 생각하면 된다.
for (size_t i = 0; i < count; i++)
{
for (size_t j = 0; j < count; j++)
{
/* code */
}
}
위와 같은 코드가 있다고 했을 때, count 수에 따라 n, n-1, n-2 ….. 0를 반복한다. 이를 중첩 수행하므로 T = n(n+1)/2이다. T= n^2^/2 + n/2이고 최고 복잡도를 기준으로 분모를 제거하면… O(n^2^)이 된다.
for (size_t i = 0; i < count; i+j)
{
for (size_t j = i+1; j < k; j++)
{
/* code */
}
}
위의 경우엔 얼핏보기에 O(n^2^) 처럼 보이겠지만 실상 그렇지 않다. worst case를 기준으로 i 는 i+k횟수만큼 증가하므로 수행횟수는 ($\frac{N}{K}$) x K (j의 worst case) 이므로 O(n)이 된다.
이전에서도 다뤘 듯, 이진탐색은 log2^N^ 만큼의 시간 복잡도를 가진다. 상수시간 K의 이진탐색 횟수에 따라 (N+($\frac{N}{2}$) + ($\frac{N}{4}$)+($\frac{N}{8}$)… ($\frac{N}{2^K}$) 의 시행횟수를 가진다.
($\frac{N}{2^K}$) = 1, (N = 2^k^) => (K = Log2^N^)의 값이 된다.
int binarySearch(int arr[], int l, int r, int x)
{
while (l <= r) {
int m = l + (r - l) / 2;
if (arr[m] == x)
return m;
if (arr[m] < x)
l = m + 1;
else
r = m - 1;
}
return -1;
}
온라인 코딩테스트에서 시간 복잡도 제한이 있을 때, 기본적으로 1s이다. 1s => 10^8^ 만큼의 반복을 수행한다.
이때 입력수가 충분히 많이 (10^9^) 주어졌을때, Linear Search등을 사용할 경우 10^9^ => 10s 의 시간복잡도를 가지기에 Logarithmic한 알고리즘이 요구되는걸 알 수 있다.
MergeSort는 기본적으로 분할 정복 과정을 거친다.
void mergeSort(int array[], int const begin, int const end)
{
// excute k times
if (begin >= end)
return;
int mid = begin + (end - begin) / 2;
mergeSort(array, begin, mid); // T(N/2)
mergeSort(array, mid + 1, end); // T(N/2)
merge(array, begin, mid, end); // n
}
T(N) = K + T($\frac{N}{2}$) + T($\frac{N}{2}$) + Kn => K(n+1) + 2T($\frac{N}{2}$) 이므로.. T(N) = Kn +2T($\frac{N}{2}$) 이 된다.
결과적으로 MergeSort는 입력 값에서 반을 나누고 (Log^n^) 합치는(N) 과정을 거치므로 NLog2^n^이 된다.
int power(int a, int n)
{
if() return 1;
return a* power(a,n-1);
}
Recursion에서 n의 조건에 따라 n회만큼 재귀적 호출이 일어난다. (Call Stack에 n만큼 스택이 쌓인다.) 시간 복잡도는 O(n)
int power(int a, int n)
{
if() return 1;
int halfPowerSquare = power(a,n/2) * power(a,n/2);
if(n%2 != 0)
return a* halfPowerSquare;
return halfPowerSquare;
}
실질적으로 1번의 경우와 같다 시간복잡도는 O(n), 공간 복잡도는 O(Log^n^)을 가진다.
수행 횟수는 Log^n^으로 1+2+4….+ 2^Logn^으로 2^Log n^ 이며 O(n)이 된다. 공간 복잡도는 Call Stack이 쌓임에 따라 늘어나는데, 분할된 Max Depth만큼 복잡도를 가지기에 Log^n^이 된다.
int power(int a, int n)
{
if() return 1;
int halfPower = power(a,n/2);
int halfPowerSquare = halfPower * halfPower;
if(n%2 != 0)
{
return a* halfPowerSquare;
}
return halfPowerSquare;
}
위 로직은 공간 및 시간 복잡도 모두 Log^n^을 가진다. a^n^-> (a^n/2^)^2^…. 의 연산 횟수를 가진다. T(n) = K + T($\frac{N}{2}$) T($\frac{N}{2}$) = K + T($\frac{N}{4}$) …. => KLog^n^을 가지기에 O(Log^n^)이 된다.
tags: Algorithm, Data Structure