Vectors and Grids
Les vecteurs sont de simples structures de données pour stocker des données
de même type. Ils sont implémentés dans la plupart des langages de
programmation. Juste avec cette structure on peut résoudre une variété de
problèmes intéressants. Ils forment la base des collections, et tout programmer
sérieux doit être un gourou
dans leurs manipulation.
Table de matières
Vecteurs statiques
Dans cette section, on introduit la notion simple de vecteur statique en C++.
Boite de DVD
On suppose qu’on dispose d’un ensemble de DVDs et qu’on veut les réaranger (d’une manière compacte).
Quel sera le meilleur choix pour stocker ces DVD
- On peut trouver un grand casier pour mettre tous ces DVD?
- Le problème est qu’il est possible d’ajouter quelque uns, ou de vouloir se débarrasser d’autres.
- Une autre considération, est que ce boitier doit contenir que des DVD et non pas d’autre objets.
- Ainsi, ce boitier doit contenir des objets de même type. (i.e des objets qui partagent les mêmes propriétés). Pour nous ces propriétés peuvent être:
- Chaque DVD doit être dans son propre couverture.
- Cette couverture porte le nom du DVD, le cast et d’autre détails.
- Toutes les couvertures doivent avoir la même taille.
Supposons qu’on doit construire une application qui gère tous nos DVD qui sont dans notre inventaire. On doit choisir pour chaque DVD les propriétés qui le représente. Aussi, on doit décider le nombre maximal de DVD qu’on peut stocker dans notre inventaire.
Ce nombre est très important car il va nous permettre de choisir la taille de notre inventaire.
En programmation, une structure idéale pour stocker ces DVD est un vecteur
array
.
Définition Vecteur.
Un vecteur (array) est une collection d’objets. Ces objets peuvent être des entiers, string, DVD, jeux ou livres, ect. Les objets sont stockés d’une manitère contigue dans la mémoire. Ceci permet un accès rapide à chaque composante de ce vecteur.
Comment peut on prendre cette définition et l’appliquer à des DVDs. Notre sens inné de structure, nous pousse à réarranger notre collection ( l’un à coté de l’autre). Nous les rangeons ainsi, pour nous faciliter la recherche de l’un de ces DVD.
En C++, les tableaux possèdent une taille N
. Cette valeur doit être
déclarée au début. Aussi on doit spécifier le type partagé par les
éléments de ce tableau.
//Déclaration d'un tableau des entiers
int vec[15];
//taille peut être une variable, mais un fois fixé
// Elle change pas
int N;
cout<<"Donner la taille: "
cin>>N
float vec2[N];
Voici un programme qui déclare la structure DVD et un vecteur pour stocker une collection de ces DVD.
#include <iostream>
#include <string>
using namespace std;
//Structure video
struct DVD
{
string name;
string director;
int year;
};
int main(int argc, char *argv[])
{
//Store a line for a dvd
string line;
//Getting the number of dvds
int N;
getline(cin, line);
N = stoi(line);
//Array of dvd
DVD inventory[N];
for(int i=0; i<N; i++)
{
getline(cin, line);
inventory[i] = from_line(line);
}
for(auto dvd: inventory)
cout<<dvd<<endl;
return 0;
}
Accès aux éléments
Les deux opérations primitives pour un tableau sont l’écriture et la lecture dans ces composantes. Le reste des opérations est fondé sur ces opérations.
Pour ces deux opérations on utilise l’opérateur d’indexage [i]
qui donne
une référence sur le ième élément. Les éléments sont ordonnés de l’indice 0
à N-1
.
#Obtenir les données du premier DVD
auto dvd1 = inventory[0];
//Modifier les données de ce dvd
inventory[0].year = 2000;
Aussi pour parcourir ce tableau, on possède des trois boulces de base, traité dans le chapitre des string.
for(int i=0 ; i<N; i++)
//Utiliser inventory[i]
for(auto dvd: inventory)
//utiliser dvd
for(auto &dvd: inventory)
//utiliser dvd
Capacité et longueur
Une question intéressante qui se pose est:
Si une personne vous demande quelle la taille de votre collection, quel sera votre réponse.
Il y as deux réponses possibles pour cette question:
- Le nombre d’éléments maximal que la collection peut contenir.
- Le nombre d’élément actuel qui sont dans la collection.
Les deux réponses sont correctes et possède une signification différente
qu’on doit maitriser. La première est appellée capacité
du tableau, alors
que la deuxième est la longueur
`.
//Tableaud de capacité 100
int tab[100];
//insérer quelque éléments
tab[0] = 3; //[lenght=1, capacity=100]
tab[1] = 4; //[lenght=2, capacity=100]
tab[2] = 5; //[lenght=3, capacity=100]
Passage d’un tableau
Un tableau (pure) en C++ ne connait ni sa capacité ni sa longueur. Ainsi une bonne pratique est de passer ces paramètres aussi à la fonction.
Un tableau est une adresse, le seul mode de passage est celui par adresse!!!
#include <iostream>
using namespace std;
void augment(int grades[], int capacity)
{
//Essayer la boucle foreach pour une erreur classique
for(int i=0; i<capacity; i++)
grades[i]++;
}
int main(int argc, char *argv[])
{
//Array with capacity 5
int grades[5]{14, 16, 18, 10, 20};
//Call with a function
augment(grades, 5);
//Printing
for(auto v: grades)
cout<<v<<" ";
cout<<endl;
return 0;
}
Exercice d’application.
Pour un exercice d’application, on se propose d’écrire une fonction
qui reçoit
un tableau et qui rend le nombre des entiers contenant un nombre pair de
chiffres.
Exemple 1:
Input: nums = [12,345,2,6,7896]
Output: 2
Explications:
12 contient 2 chiffres.
345 contient 3 chiffres.
2 contient 1 chiffres.
6 contient 1 chiffres.
7896 contient 4 chiffres.
Exemple 2:
Input: nums = [555,901,482,1771]
Output: 1
Explication:
Seulement 1771 contient a nombre pair de chiffres.
Opérations de base sur les tableaux.
Maintenant qu’on possède une maitrise du fonctionnement d’un tableau et leurs stockage en mémoire. L’étape suivante est de se concentrer sur les opérations de base d’un tableau. Si on revient sur l’exemple d’inventaire de DVD, on peut s’intéresser à réaliser les opérations suivantes:
- Insérer: un nouveau DVD à notre collection.
- Supprimer: si on est plus convincu qu’il doit y être.
- Chercher: un dvd particulier.
Insertion
L’insertion est l’opération d’ajouter un élément au vecteur.
Cette insertion peut ếtre réalisée dans plusieurs emplacements.
- Insérer à la
fin
du tableau.
bool insert_end(int arr[], int &lenght, int capacity, int value)
{
//Vérifier si on peut insérer
if( lenght >= capacity)
return false;
else
{
arr[lenght++] = value;
}
}
- Insérer au
début
du tableau.
bool insert_begin(int arr[], int &lenght, int capacity, int value)
{
//Vérifier si on peut insérer
if( lenght >= capacity)
return false;
else
{
//Shifting
for(int i = lenght; i>0; i--)
arr[i]= arr[i-1];
//Insert element
arr[0] = value;
//Dont forget to increase the lenght
lenght++;
}
}
- Insérer à un
indice
donné.
bool insert_at(int arr[], int &lenght, int capacity, int value, int index)
{
//Vérifier si on peut insérer
if( lenght >= capacity)
return false;
else
{
//Shifting
for(int i = lenght; i>index; i--)
arr[i]= arr[i-1];
//Insert element
arr[index] = value;
//Dont forget to increase the lenght
lenght++;
}
}
Deletion
Contrairement à la suppression, on peut s’intéresser à enlever des objets de note inventaire (tableau). On peut supprimer un élément selon les trois cas ( début, indice, fin). On choisit de regrouper les trois opérations en celle qui est générale (suppression à un undice donnée).
bool delete_at(int arr[], int &lenght , int index)
{
if( index < 0 || index>= lenght)
return false;
//shift back
for(int i=index; i<lenght-1; i++)
arr[i] = arr[i+1];
//lenght decreases
lenght--;
//Deletion is successful
return true;
}
Search
Finalement, nous allons traiter l’opération la plus importante. A plusieurs reprise, on aurait besoin de connaitre si un élément existe dans une collection. Généralement cette question à elle seule décide le type de collection qu’on doit utiliser.
Il yas plusieurs méthodes pour chercher dans un tableau, pout l’instant, on va
se concentrer sur la méthode la plus basique. La recherche dans un tableau
consiste à trouver une occurence d’un élément particulier et renvoie sa
position
. Si on connait la position de cet élément, on peut accèder
directement à sa case dans un temps constant \(\mathcal{O}(1)\) (explication
chapitre compléxité).
Recherche linéaire
: Dans cette recherche, on commence par le premier élément,
si on trouve l’élément cherché, on arrête et on renvoie cette position. Sinon on
passe à l’élément suivant. Si on atteint la fin et qu’on trouve pas, on déclare
le drapeau d’échec de la recherche et on renvoie l’indice \(-1\).
int linear_search(int arr[], int lenght, int target)
{
//Simple loop
for(int i=0; i<lenght; i++)
if( arr[i] == target)
return i;
//Echec
return -1;
}
Qu’on on introduit la notion de récurrence, on va revisiter cette recherche en appliquant la recherche dichotomique (binary search) qui est plus efficace si le tableau est trié.