void f(int n, int m){
if (n <= 1) {
int *arr=malloc(m*sizeof(int));
for (int i=0; i<m; i++) arr[i] = 0;
free(arr);
return;
}
f(n-1, m+1);
f(n%2, m+1);
}
Давайте проведем рефакторинг:
void f1(int m) {
int *arr = malloc(m*sizeof(int));
for (int i = 0; i < m; i++) {
arr[i] = 0;
}
free(arr);
}
void f(int n, int m){
if (n <= 1) {
f1(m);
return;
}
f(n-1, m+1);
f(n%2, m+1);
}
Так что для f1 это довольно просто, - сложность пространства равна sizeof(int) * m
- нам нужно выделить это много - а сложность времени всего лишь m
-мы перебираем все m
элементы в массиве arr
.
. n%2
может быть только 1
или 0
, поэтому мы можем заменить f(n%2, m+1);
на f1(m+1)
.
void f(int n, int m){
if (n <= 1) {
f1(m); // (1)
return;
}
f(n-1, m+1); // (2)
f1(m + 1); // (3)
}
Сейчас.Если n > 1
, то мы звоним f(n-1, ...
до n <= 1
.Для каждого n > 1
мы вызываем f1(m + 1)
в обратном хронологическом порядке (потому что это после рекурсивного вызова).Когда мы добираемся до n <= 1
, тогда f1(m)
вызывается с m = m(initial) + n(initial) - 1
разами.Ох, может быть, например, пример для n=5
, тогда:
- начальный вызов
f(5, m)
, поэтому n = 5 - n = 5, поэтому мы вызываем
f(4, m+1)
// (2) - n = 4, поэтому мы называем
f(3, m+2)
// (2) - n = 3, поэтому мы вызываем
f(2, m+3)
// (2) - n = 2, поэтому мы вызываем
f(1, m+4)
// (2) - n = 1, поэтому мы вызываем
f1(m+4)
и возвращаем // (1) - n = 2,после (2), поэтому мы вызываем
f1(m+4)
// (3) - n = 3, после (2), поэтому мы вызываем
f1(m+3)
// (3) - n =4, после (2), поэтому мы вызываем
f1(m+2)
// (3) - n = 5, после (2), поэтому мы называем
f1(m+1)
// (3)
Мы можем видеть, что f1(m+4)
вызывается дважды, и что мы вызываем f1(m + i)
в обратном порядке от i=1
до i=4
.
Мы можем "раскрыть" функцию:
void f(int n, int m){
f1(m + n - 1);
for (int i = n - 1; i > 0; --i) {
f1(m + i);
}
}
Когда m
и n
приближаются к бесконечности, +1
или -1
ничего не значат.
Пространственная сложность - это сложность пространства f1(max(m + i, m + n - 1))
, потому что f1
освобождает память каждый раз.Так что (m + n - 1) * sizeof(int)
, что составляет (m + n) * sizeof(int)
, то есть m + n
.
Сложность времени зависит от того, сколько раз мы вызываем функцию f1
.Мы видим, что мы называем:
f1(m + n - 1)
f1(m + n - 1)
f1(m + n - 2)
...
f1(m + 2)
f1(m + 1)
Таким образом, сложность времени составляет
(m + n - 1) + ((m + n - 1) + (m + n - 2) + ... + (m + 1))
(m + n - 1) + (n - 1) * m + ((n - 1) + (n - 2) + ... 1)
(m + n - 1) + (n - 1) * m + ((n - 1) * (n - 1 + 1) / 2)
(m + n - 1) + (n - 1) * m + ((n - 1) * (n - 1 + 1) / 2)
// the `*2`, `/2`, `+1` and `-1` mean nothing close to infinity
m + n + n * m + n * n
m + n + m * n + n * n
m * (n + 1) + n * (n + 1)
(m + n) * (n + 1)
(m + n) * n