tl; dr: Вы правы в обоих случаях. Первый код O(n)
. Второй - O(nlogn)
.
Ваш друг ошибается, потому что он не понимает, что первый код имеет экспоненциальный спад, а второй - нет, что очень важно при анализе сложности.
Подробные объяснения ниже.
Первый код:
For i = 1 -> j runs 1 times
For i = 2 -> j runs 2 times
For i = 4 -> j runs 4 times
...
for i = 2^k -> j runs 2^k times.
Это означает, что сложность первого кода:
1 + 2 + 4 + ... + 2^k + ... + 2^(logn)
Это Geometri c серия с:
a = 1
r = 2
N = logn + 1
Сумма геометрии c серии задается как:
a(1-r^N)/(1-r) =
1 * (1-2^(logn+1))/(1-2) = 1*(2^(logn+1) - 1) = 2*2^logn - 1 = 2n - 1
И это легко увидеть, что это в O(n)
.
Второй код:
For i = 1 -> j runs log(1) times
For i = 2 -> j runs log(2) times
For i = 3 -> j runs log(3) times
....
For i = k -> j runs log(k) times
Если мы сложим это, мы получим
log(1) + log(2) + log(3) + ... + log(k) + log(n)
Это совершенно другое от предыдущего. Нет распада геометрии c, поэтому сложность не линейна.
Простой способ увидеть это:
log(1) + log(2) + log(3) + ... + log(k) + log(n) >
> log(n/2) + log(n/2 + 1) + log(n/2 + 2) + ... + log(n/2 + n/2) >
> n/2 * log(n/2) >=
>= n/2 * log(n) - n/2*log(2)
Поскольку это O(nlogn)
, сложность этого кода в O(nlogn)
(Другой способ показать это, перейдя к log(n!)
, который известен O(nlogn)
)