Mesures de complexités pour des méthodes et des structures

Lorsque l'on code un algorithme, on a coutume (et c'est très bien) d'utiliser des fonctions, des méthodes ou des structures de données que l'on n'a pas écrites. Il faut en revanche bien connaître leurs complexités pour ne pas commettre d'erreur de calcul.

À retenir

Lorsque l'on calcule une complexité toutes les méthodes et fonctions doivent être examinées.

Complexité de structure

En informatique, les objets que l'on manipule ont des types. On connaît déjà des objets basiques qui sont de types booléens, entiers, réels ou encore chaines de caractères pour lesquels toutes les opérations basiques que l'on peut effectuer avec eux sont en $\mathcal{O}(1)$ opérations. Ce n'est plus le cas lorsque l'on utilise des type plus complexes, composé de types basiques comme les tableaux, ou encore les listes de python. Pour pouvoir calculer la complexité d'un algorithme les utilisant, il faut connaître les complexités de ses opérations. Souvent, les opérations suivantes suffisent :

À retenir

Pour chaque type de donnée, il faut connaître la complexité de :

  • la création d'un objet de ce type
  • la suppression d'un objet de ce type
  • chaque méthode liée au type

Tableaux

Prenons le type tableau comme exemple. Un tableau est un conteneur pouvant contenir $n$ objets (on appelle $n$ la taille d'un tableau). On peut accéder et affecter un objet au tableau grâce à un indice allant de $0$ à $n-1$ : si t est un tableau t[i] correspond à l'objet d'indice $i$ du tableau.

Avec un tableau on peut faire uniquement 3 choses :

il est impossible de redimensionner un tableau. Sa taille est fixée à la création. Toute méthode qui vise à augmenter ou diminuer la taille d'un tableau recrée un nouveau tableau et copie tous les éléments de l'ancien tableau dans le nouveau.

Listes

Le langage python ne connaît pas les tableaux. Il utilise le type liste à la place. Une liste peut être vue comme l'évolution du type tableau. On donne ici juste les complexités de cette structure pour que vous puissiez les utiliser dans vos programmes :

À retenir

Une liste peut-être vue comme un tableau dont on peut augmenter ou diminuer la taille par la fin en $\mathcal{O}(1)$ opérations.

Ne confondez pas liste et liste chaînée ce n'est pas du tout la même structure !

Opérations sur les conteneurs

On a dit que l'on pouvait considérer que la création d'une liste, d'un tableau et d'une chaîne de caractères comme valant $\mathcal{O}(1)$. Ceci était un raccourci qu'il nous faut maintenant expliciter car il peut induire en erreur lorsque l'on considères des opérations sur les conteneurs comme la concaténation.

Les opérations de création d'un conteur (comme un tableau, une liste, un ensemble, ou encore un dictionnaire) possédant $K$ objets est usuellement de complexité en $\mathcal{O}(K)$.

Si $K$ est une constante la complexité de création est bien $\mathcal{O}(1)$. Comme dans le cas suivant :

x = [1, 2, 3]

Mais si $K$ n'est pas une constante, comme dans le cas ci-après, on ne peut plus assimiler $\mathcal{O}(K)$ à $\mathcal{O}(1)$ :

def duplique(x):
  return list(x)

La complexité de la fonction duplique(x: list) -> list n'est pas $\mathcal{O}(1)$ mais bien $\mathcal{O}(\text{len}(x))$.

La complexité des opérations créant des conteneurs dépend toujours de leurs tailles.

De là :

Fonctions et méthodes données

Il faut connaître les différentes complexités des méthodes et fonctions utilisées. Ne vous laissez pas méprendre. Ce n'est pas parce qu'elle font 1 seule ligne que leur complexité est en $\mathcal{O}(1)$. Par exemple la complexité de la méthode max de python, qui prend en entrée une liste l :

l = [1, 3, 2, 6, 4, 5]
print(l.max())

Est de complexité $\mathcal{O}(n)$ où $n$ est la taille de la liste l et pas $\mathcal{O}(1)$. Il faut en effet parcourir tous les éléments d'une liste (a priori non triée) pour en trouver le maximum.

Lorsque vous utilisez des fonctions et des méthodes en python, il faut toujours vérifier la complexité de celles-ci. Ce n'est pas toujours $\mathcal{O}(1)$.