Jeśli chcesz przeanalizować te algorytmy, musisz zdefiniować // dostuff, ponieważ to może naprawdę zmienić wynik. Załóżmy, że dostuff wymaga stałej liczby operacji O (1).
Oto kilka przykładów z tą nową notacją:
Dla twojego pierwszego przykładu, przejście liniowe: jest to poprawne!
NA):
for (int i = 0; i < myArray.length; i++) {
myArray[i] += 1;
}
Dlaczego jest liniowy (O (n))? W miarę dodawania do elementu (tablicy) dodatkowych elementów ilość wykonywanych operacji rośnie proporcjonalnie do liczby dodawanych elementów.
Jeśli więc potrzeba jednej operacji, aby zwiększyć liczbę całkowitą gdzieś w pamięci, możemy modelować pracę wykonywaną przez pętlę za pomocą f (x) = 5x = 5 dodatkowych operacji. Dla 20 dodatkowych elementów wykonujemy 20 dodatkowych operacji. Pojedynczy przebieg tablicy jest zwykle liniowy. Podobnie są algorytmy, takie jak sortowanie kubełkowe, które są w stanie wykorzystać strukturę danych do sortowania w jednym przejściu tablicy.
Twój drugi przykład również byłby poprawny i wygląda następująco:
O (N ^ 2):
for (int i = 0; i < myArray.length; i++) {
for (int j = 0; j < myArray.length; j++) {
myArray[i][j] += 1;
}
}
W takim przypadku dla każdego dodatkowego elementu w pierwszej tablicy musimy przetworzyć WSZYSTKIE j. Dodanie 1 do i faktycznie dodaje (długość j) do j. Masz więc rację! Ten wzorzec to O (n ^ 2) lub w naszym przykładzie jest to faktycznie O (i * j) (lub n ^ 2, jeśli i == j, co często ma miejsce w przypadku operacji macierzowych lub kwadratowej struktury danych.
Trzeci przykład to sytuacja, w której wszystko zmienia się w zależności od dostuff; Jeśli kod jest taki, jak napisano, a wykonywanie rzeczy jest stałe, w rzeczywistości jest to tylko O (n), ponieważ mamy 2 przebiegi tablicy o rozmiarze n, a 2n zmniejsza się do n. Pętle znajdujące się na zewnątrz nie są kluczowym czynnikiem, który może wytworzyć kod 2 ^ n; oto przykład funkcji 2 ^ n:
var fibonacci = function (n) {
if (n == 1 || n == 2) {
return 1;
}
else {
return (fibonacci(n-2) + fibonacci(n-1));
}
}
Ta funkcja ma wartość 2 ^ n, ponieważ każde wywołanie funkcji powoduje DWA dodatkowe wywołanie funkcji (Fibonacciego). Za każdym razem, gdy wywołujemy funkcję, ilość pracy, którą musimy wykonać, podwaja się! Rośnie to bardzo szybko, jak odcięcie głowy hydrze i za każdym razem wypuszczenie dwóch nowych!
Na przykład, jeśli używasz sortowania nlgn, takiego jak scalanie, masz rację, że ten kod będzie O (nlgn). Możesz jednak wykorzystać strukturę danych do opracowania szybszych sortowań w określonych sytuacjach (np. W znanym, ograniczonym zakresie wartości, np. Od 1 do 100). Masz jednak rację, myśląc, że dominuje kod najwyższego rzędu; więc jeśli sortowanie O (nlgn) znajduje się obok dowolnej operacji, która zajmuje mniej niż czas O (nlgn), całkowita złożoność czasu wyniesie O (nlgn).
W JavaScript (przynajmniej w Firefoksie) domyślnym sortowaniem w Array.prototype.sort () jest rzeczywiście MergeSort, więc patrzysz na O (nlgn) na swój ostateczny scenariusz.