Si vous voulez analyser ces algorithmes, vous devez définir // dostuff, car cela peut vraiment changer le résultat. Supposons que dostuff nécessite un nombre O (1) constant d'opérations.
Voici quelques exemples avec cette nouvelle notation:
Pour votre premier exemple, la traversée linéaire: c'est correct!
SUR):
for (int i = 0; i < myArray.length; i++) {
myArray[i] += 1;
}
Pourquoi est-il linéaire (O (n))? Lorsque nous ajoutons des éléments supplémentaires à l'entrée (tableau), la quantité d'opérations qui se produisent augmente proportionnellement au nombre d'éléments que nous ajoutons.
Donc, s'il faut une opération pour incrémenter un entier quelque part en mémoire, nous pouvons modéliser le travail que fait la boucle avec f (x) = 5x = 5 opérations supplémentaires. Pour 20 éléments supplémentaires, nous effectuons 20 opérations supplémentaires. Un seul passage d'un tableau a tendance à être linéaire. Il en va de même pour les algorithmes comme le tri par compartiment, qui sont capables d'exploiter la structure des données pour effectuer un tri en une seule passe d'un tableau.
Votre deuxième exemple serait également correct et ressemble à ceci:
O (N ^ 2):
for (int i = 0; i < myArray.length; i++) {
for (int j = 0; j < myArray.length; j++) {
myArray[i][j] += 1;
}
}
Dans ce cas, pour chaque élément supplémentaire du premier tableau, i, nous devons traiter TOUS j. Ajouter 1 à i ajoute en fait (longueur de j) à j. Vous avez donc raison! Ce modèle est O (n ^ 2), ou dans notre exemple, il s'agit en fait de O (i * j) (ou n ^ 2 si i == j, ce qui est souvent le cas avec des opérations matricielles ou une structure de données carrée.
Votre troisième exemple est celui où les choses changent en fonction des matières premières; Si le code est tel qu'écrit et que do stuff est une constante, ce n'est en fait que O (n) car nous avons 2 passes d'un tableau de taille n, et 2n se réduit à n. Les boucles étant à l'extérieur les unes des autres ne sont pas le facteur clé qui peut produire 2 ^ n code; voici un exemple d'une fonction qui est 2 ^ n:
var fibonacci = function (n) {
if (n == 1 || n == 2) {
return 1;
}
else {
return (fibonacci(n-2) + fibonacci(n-1));
}
}
Cette fonction est 2 ^ n, car chaque appel à la fonction produit DEUX appels supplémentaires à la fonction (Fibonacci). Chaque fois que nous appelons la fonction, la quantité de travail que nous devons faire double! Cela se développe très rapidement, comme couper la tête d'une hydre et en faire germer deux nouvelles à chaque fois!
Pour votre dernier exemple, si vous utilisez un tri nlgn comme merge-sort, vous avez raison de dire que ce code sera O (nlgn). Cependant, vous pouvez exploiter la structure des données pour développer des tris plus rapides dans des situations spécifiques (par exemple sur une plage de valeurs connue et limitée, comme de 1 à 100). Vous avez cependant raison de penser que le code d'ordre le plus élevé domine; donc si un tri O (nlgn) est à côté de toute opération qui prend moins de temps O (nlgn), la complexité temporelle totale sera O (nlgn).
En JavaScript (au moins dans Firefox), le tri par défaut dans Array.prototype.sort () est en effet MergeSort, donc vous regardez O (nlgn) pour votre scénario final.