Se vuoi analizzare questi algoritmi devi definire // dostuff, in quanto ciò può davvero cambiare il risultato. Supponiamo che Dostuff richieda un numero costante di operazioni O (1).
Ecco alcuni esempi con questa nuova notazione:
Per il tuo primo esempio, l'attraversamento lineare: questo è corretto!
SU):
for (int i = 0; i < myArray.length; i++) {
myArray[i] += 1;
}
Perché è lineare (O (n))? Man mano che aggiungiamo ulteriori elementi all'input (array), la quantità di operazioni in corso aumenta proporzionalmente al numero di elementi che aggiungiamo.
Quindi, se ci vuole un'operazione per incrementare un numero intero da qualche parte nella memoria, possiamo modellare il lavoro che il ciclo fa con f (x) = 5x = 5 operazioni aggiuntive. Per 20 elementi aggiuntivi, eseguiamo 20 operazioni aggiuntive. Un singolo passaggio di un array tende ad essere lineare. Quindi sono algoritmi come l'ordinamento bucket, che sono in grado di sfruttare la struttura dei dati per eseguire un ordinamento in un singolo passaggio di un array.
Anche il tuo secondo esempio sarebbe corretto e si presenta così:
O (N ^ 2):
for (int i = 0; i < myArray.length; i++) {
for (int j = 0; j < myArray.length; j++) {
myArray[i][j] += 1;
}
}
In questo caso, per ogni elemento aggiuntivo nel primo array, i, dobbiamo elaborare TUTTO j. L'aggiunta di 1 a i aggiunge effettivamente (lunghezza di j) a j. Quindi, hai ragione! Questo modello è O (n ^ 2), o nel nostro esempio è in realtà O (i * j) (o n ^ 2 se i == j, che è spesso il caso delle operazioni con matrici o di una struttura di dati quadrata.
Il tuo terzo esempio è dove le cose cambiano a seconda del cibo; Se il codice è come scritto e fare cose è una costante, in realtà è solo O (n) perché abbiamo 2 passaggi di un array di dimensioni n e 2n si riduce a n. I loop che si trovano uno fuori dall'altro non sono il fattore chiave che può produrre codice 2 ^ n; ecco un esempio di una funzione che è 2 ^ n:
var fibonacci = function (n) {
if (n == 1 || n == 2) {
return 1;
}
else {
return (fibonacci(n-2) + fibonacci(n-1));
}
}
Questa funzione è 2 ^ n, poiché ogni chiamata alla funzione produce DUE chiamate aggiuntive alla funzione (Fibonacci). Ogni volta che chiamiamo la funzione, la quantità di lavoro che dobbiamo fare raddoppia! Questo cresce molto rapidamente, come tagliare la testa di un'idra e far spuntare due nuovi ogni volta!
Per il tuo esempio finale, se stai usando un ordinamento nlgn come merge-sort, hai ragione che questo codice sarà O (nlgn). Tuttavia, è possibile sfruttare la struttura dei dati per sviluppare ordinamenti più rapidi in situazioni specifiche (come in un intervallo di valori noto e limitato come 1-100). Si ha ragione nel pensare, tuttavia, che domini il codice di ordine più elevato; quindi se un ordinamento O (nlgn) è vicino a qualsiasi operazione che impiega meno del tempo O (nlgn), la complessità del tempo totale sarà O (nlgn).
In JavaScript (almeno in Firefox) l'ordinamento predefinito in Array.prototype.sort () è effettivamente MergeSort, quindi stai guardando O (nlgn) per il tuo scenario finale.