Какова временная сложность приведенного ниже фрагмента кода?

#algorithm #time-complexity

#алгоритм #временная сложность

Вопрос:

Это похоже на какую-то частичную сортировку.

 int n = a.length;
for(int i = 0; i < n; i  ) {
    while(a[i] != i) {
        if(a[i] < 0 || a[i] >= n) //avoid stepping out of range
            break;
        if(a[i] == a[a[i]]) //avoid inf loop by duplicates
            break;
        int t = a[i];
        a[i] = a[t];
        a[t] = t;
    }
}
        
return a;
  

На первый взгляд кажется, что O (N ^ 2), но когда я запускаю, кажется, что O (N) . Есть идеи? Заранее спасибо.

Комментарии:

1. @Elliott Никакая другая обработка массива не выполняется, например, a = {1, 2, 100, 200}.

2. Внутренний цикл while не зависит от n . Поэтому оно не может быть O (n ^ 2).

3. @lincr, внешний цикл? Число уменьшается как минимум на единицу. Тем не менее, только по этой логике это все еще может быть O(n^2) .

Ответ №1:

Вы правы, что это O(n) :


Чтобы помочь объяснить это, я составлю определение:

Отражающий: элемент a[i] в массиве a является отражающим, если a[i] = i .


Итерации while цикла, которые приводят к break :

Для каждого значения i вы можете иметь ровно одно break значение, которое выполняется в while цикле (включая while условие). Поскольку есть n значения i , это означает, что n общее количество итераций while цикла приводит к break .

Итерации while цикла, которые не приводят к break :

Для этой части может помочь представить наш массив, в котором каждый элемент является либо отражающим ( 1 ), либо неотражающим ( 0 ):

| 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 |

Как только мы пройдем break точки, мы узнаем, что a[i] != a[a[i]] (т.е. если мы назовем a[i] as t , то мы это знаем a[t] != t ). И поскольку мы позже присваиваем a[t] = t , то мы изменили элемент массива с неотражающего на отражающий. Обратите внимание, что нигде в вашем коде мы не делаем отражающий элемент неотражающим: присвоение a[i] = a[t] может привести к a[i] неотражению, но мы также знаем, что оно не было отражающим с самого начала, потому while что утверждение было истинным : a[i] != i .

Из нашего визуального представления это означает, что 1 a никогда не изменяется 0 , и все же каждая итерация while цикла (которая передает break точки) приводит по крайней мере к одному 0 переходу к a 1 .

break Как только вы заметите, что каждая (не) итерация внутреннего цикла берет по крайней мере один (возможно, два) неотражающих элемента из массива и преобразует его в постоянно n отражающий, тогда мы понимаем, что общее количество (непрерывных) итераций внутреннего цикла не может превышать для всеговремя выполнения программы.


Вкратце: i повторяется и проверяется во время for цикла n , и каждый выполняет постоянный объем работы, c1 . Существует n общее количество итераций while цикла, соответствующих a break , и не более n итераций, которые не соответствуют a break . Следовательно, всего существует не более 2n итераций while цикла. Работа, выполняемая за одну итерацию while цикла, является некоторой максимальной константой, c2 .

Следовательно, временная сложность <= c1*n c2*2*n = O(n) .


Что касается функции кода, она перестраивает элементы, чтобы сделать как можно больше из них отражающими: если after this функция a[i] неотражающая, то значение i отсутствует в массиве.

Комментарии:

1. Мне нравятся ответы, которые выходят за рамки строки кода или формулы путем объяснения. Приятно. 😉