web-dev-qa-db-fra.com

Pourquoi utilisons-nous des tableaux plutôt que d'autres structures de données?

Pendant que je programmais, je n’ai pas vu de cas où un tableau est meilleur pour stocker des informations qu’une autre de ses formes. J'avais en effet pensé que les "fonctionnalités" ajoutées dans les langages de programmation avaient été améliorées et remplacées par celles-ci. Je vois maintenant qu'ils ne sont pas remplacés, mais ont plutôt une nouvelle vie, pour ainsi dire.

Donc, en gros, à quoi ça sert d'utiliser des tableaux? 

Ce n'est pas tant pourquoi nous utilisons les tableaux du point de vue de l'ordinateur, mais plutôt pourquoi devrions-nous utiliser les tableaux du point de vue de la programmation (une différence subtile). Ce que l'ordinateur fait avec le tableau n'était pas le but de la question.

189
Xesaniel

Il est temps de remonter dans le temps pour une leçon. Bien que nous ne pensions pas beaucoup à ces choses dans nos langages gérés sophistiqués actuels, ceux-ci sont construits sur les mêmes bases. Voyons donc comment la mémoire est gérée en C.

Avant de plonger, une brève explication de la signification du terme "pointeur". Un pointeur est simplement une variable qui "pointe" sur un emplacement en mémoire. Il ne contient pas la valeur réelle dans cette zone de mémoire, il contient l'adresse de la mémoire. Pensez à un bloc de mémoire comme une boîte aux lettres. Le pointeur serait l'adresse de cette boîte aux lettres.

En C, un tableau est simplement un pointeur avec un décalage, le décalage spécifiant à quelle distance en mémoire regarder. Ceci fournit O(1) temps d'accès. 

  MyArray   [5]
     ^       ^
  Pointer  Offset

Toutes les autres structures de données s'appuient sur cela ou n'utilisent pas la mémoire adjacente pour le stockage, ce qui entraîne un temps de recherche d'accès aléatoire insuffisant (bien qu'il y ait d'autres avantages à ne pas utiliser de mémoire séquentielle).

Par exemple, supposons que nous ayons un tableau avec 6 nombres (6,4,2,3,1,5), en mémoire cela ressemblerait à ceci:

=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================

Dans un tableau, nous savons que chaque élément est côte à côte en mémoire. Un tableau C (appelé MyArray ici) est simplement un pointeur sur le premier élément:

=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================
   ^
MyArray

Si nous voulions rechercher MyArray [4], on y accèderait de la manière suivante:

   0     1     2     3     4 
=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================
                           ^
MyArray + 4 ---------------/
(Pointer + Offset)

Dans la mesure où nous pouvons accéder directement à n'importe quel élément du tableau en ajoutant le décalage au pointeur, nous pouvons rechercher tous les éléments dans le même temps, quelle que soit la taille du tableau. Cela signifie que l'obtention de MyArray [1000] prendrait autant de temps que celle d'obtenir MyArray [5].

Une autre structure de données est une liste chaînée. Ceci est une liste linéaire de pointeurs, chacun pointant vers le noeud suivant

========    ========    ========    ========    ========
| Data |    | Data |    | Data |    | Data |    | Data |
|      | -> |      | -> |      | -> |      | -> |      | 
|  P1  |    |  P2  |    |  P3  |    |  P4  |    |  P5  |        
========    ========    ========    ========    ========

P(X) stands for Pointer to next node.

Notez que j'ai fait chaque "noeud" dans son propre bloc. C'est parce qu'ils ne sont pas garantis d'être (et ne seront probablement pas) adjacents en mémoire.

Si je veux accéder à P3, je ne peux pas y accéder directement, car je ne sais pas où il se trouve dans la mémoire. Tout ce que je sais, c'est où se trouve la racine (P1). Je dois donc commencer par P1 et suivre chaque pointeur vers le nœud souhaité. 

Il s’agit d’un temps de O(N)] (le coût de la recherche augmente à mesure que chaque élément est ajouté). Se rendre à P1000 coûte beaucoup plus cher que se rendre à P4.

Les structures de données de niveau supérieur, telles que les tables de hachage, les piles et les files d'attente, peuvent toutes utiliser un tableau (ou plusieurs tableaux) en interne, tandis que les listes liées et les arbres binaires utilisent généralement des nœuds et des pointeurs.

Vous pourriez vous demander pourquoi quelqu'un utiliserait une structure de données nécessitant une traversée linéaire pour rechercher une valeur plutôt que d'utiliser simplement un tableau, mais ils ont leurs utilisations.

Prenez notre tableau à nouveau. Cette fois, je veux trouver l’élément de tableau qui contient la valeur '5'.

=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================
   ^     ^     ^     ^     ^   FOUND!

Dans cette situation, je ne sais pas quel décalage ajouter au pointeur pour le trouver. Je dois donc commencer à 0 et continuer jusqu'à ce que je le trouve. Cela signifie que je dois effectuer 6 contrôles.

De ce fait, la recherche d'une valeur dans un tableau est considérée comme un O (N). Le coût de la recherche augmente à mesure que le tableau s'agrandit. 

Rappelez-vous ci-dessus où j'ai dit que parfois, l'utilisation d'une structure de données non séquentielle peut avoir des avantages La recherche de données est l'un de ces avantages et l'un des meilleurs exemples est l'arbre binaire.

Une arborescence binaire est une structure de données similaire à une liste chaînée. Cependant, au lieu de se lier à un seul nœud, chaque nœud peut être lié à deux nœuds enfants.

         ==========
         |  Root  |         
         ==========
        /          \ 
  =========       =========
  | Child |       | Child |
  =========       =========
                  /       \
            =========    =========
            | Child |    | Child |
            =========    =========

 Assume that each connector is really a Pointer

Lorsque les données sont insérées dans une arborescence binaire, plusieurs règles sont utilisées pour décider où placer le nouveau nœud. Le concept de base est que si la nouvelle valeur est supérieure à celle des parents, elle l'insère à gauche, si elle est inférieure, elle l'insère à droite.

Cela signifie que les valeurs dans un arbre binaire pourraient ressembler à ceci:

         ==========
         |   100  |         
         ==========
        /          \ 
  =========       =========
  |  200  |       |   50  |
  =========       =========
                  /       \
            =========    =========
            |   75  |    |   25  |
            =========    =========

Lors de la recherche d'un arbre binaire pour la valeur 75, il suffit de visiter 3 nœuds (O (log N)) à cause de cette structure:

  • Est-ce que 75 est inférieur à 100? Regardez le bon noeud
  • Est-ce que 75 est supérieur à 50? Regardez le nœud gauche
  • Il y a les 75!

Même s'il y a 5 nœuds dans notre arbre, nous n'avons pas besoin de regarder les deux autres, car nous savions qu'ils (et leurs enfants) ne pourraient pas contenir la valeur que nous recherchions. Cela nous donne un temps de recherche qui, dans le pire des cas, nous oblige à visiter chaque nœud, mais dans le meilleur des cas, nous ne devons visiter qu'une petite partie des nœuds.

C'est là que les tableaux sont battus, ils fournissent un temps de recherche linéaire O(N), malgré le temps d'accès O(1).

Il s'agit d'une vue d'ensemble incroyablement détaillée des structures de données en mémoire, ignorant de nombreux détails, mais qui, espérons-le, illustre la force et la faiblesse d'un tableau par rapport à d'autres structures de données.

756
FlySwat

Pour O(1) accès aléatoire, qui ne peut pas être battu.

72
jason

Tous les programmes ne font pas la même chose ou ne fonctionnent pas sur le même matériel.

C’est généralement la raison pour laquelle diverses fonctionnalités linguistiques existent. Les tableaux sont un concept informatique de base. Remplacer des tableaux par des listes/matrices/vecteurs/toute structure de données avancée aurait un impact important sur les performances et serait carrément impraticable dans un certain nombre de systèmes. Il existe un certain nombre de cas où l'utilisation de l'un de ces objets de collecte de données "avancés" doit être utilisée à cause du programme en question. 

En programmation d'entreprise (comme la plupart d'entre nous le faisons), nous pouvons cibler un matériel relativement puissant. Utiliser une liste en C # ou Vector en Java est le bon choix dans ces situations, car ces structures permettent au développeur d'atteindre plus rapidement les objectifs, ce qui permet à ce type de logiciel d'être plus détaillé.

Lors de l'écriture d'un logiciel intégré ou d'un système d'exploitation, un tableau peut souvent être le meilleur choix. Bien qu'un tableau offre moins de fonctionnalités, il utilise moins de RAM et le compilateur peut optimiser le code plus efficacement pour les recherches dans les tableaux.

Je suis sûr que je laisse de côté un certain nombre d'avantages pour ces cas, mais j'espère que vous avez compris.

20
Jason Jackson

Une façon d'examiner les avantages des tableaux consiste à déterminer où se trouve la capacité d'accès aux tableaux O(1) et, par conséquent, à capitaliser:

  1. Dans les tables de recherche de votre application (tableau statique permettant d'accéder à certaines réponses catégoriques)

  2. Memoization (résultats de la fonction complexe déjà calculés, afin que vous ne calculiez plus la valeur de la fonction, disons log x)

  3. Applications de vision par ordinateur haute vitesse nécessitant un traitement de l'image ( https://en.wikipedia.org/wiki/Lookup_table#Lookup_tables_in_image_processing )

0
priya khokher