Vous êtes sur la page 1sur 10

République Algérienne Démocratique et Populaire

Ministère de l'Enseignement Supérieur et de la Recherche Scientifique

Université Akli Mohand Oulhadj de Bouira


Faculté des Sciences et des Sciences Appliquées

Département d'Informatique

Projet Module
Structure de données avancées
Spécialité : ISIL

Thème

Skip List
Réalisé par :

2020/2021
Sommaire

1 Introduction

1.1 Liste chaînée 


1.2 Skip List 

2 Les algorithmes

2.1 La recherche
2.2 L’Insertion
2.3 La suppression
2.4 La hauteur

3 La complexité
1 Introduction
En informatique, il est courant de vouloir enregistrer des données
quelconques. Une structure de données est une représentation logique de ces
données. La manière de représenter les données permet de résoudre différents
problèmes. Une structure précise peut être plus performante pour un cas précis
ou, au contraire, être très peu performante. D’autre part, une meilleure
organisation de l’information peut aussi réduire de façon significative la
complexité d’un algorithme.
Il existe un grand nombre de structures de données, toutes proposent aux
concepteurs de logiciel deux fonctionnalités standard : enregistrer et récupérer
un renseignement. Selon l’approche employée pour la représentation logique, les
performances seront différentes. Certaines structures de données offrent la
possibilité de récupérer le premier élément en temps constant, au détriment du
temps d’accès aux autres éléments. Alors que d’autres permettent un accès en
temps constant à chacun des éléments en dépit d’une durée plus longue pour
l’enregistrement d’une information.

1.1 Liste chaînée :


Une liste chaînée est une structure de données de taille variable. Le principe
est que chacun des éléments de cette liste contient une référence vers l’élément
suivant. Cette pratique facilite l’insertion et la suppression des éléments, au
détriment de la rapidité de la recherche.
On dit liste chaînée car les données sont chaînées es unes avec les autres. On
accède aux données à l’aide d’un ou deux points d’entrées qui se situent la
plupart du temps aux extrémités de la List.

Figure 1 : Liste chaînée


1.2 Skip List :
En bref, les listes de sauts sont une structure de type liste liée qui permet une
recherche rapide. Il se compose d'une liste de base contenant les éléments, ainsi
que d'une tour de listes conservant une hiérarchie liée de sous-séquences,
chacune sautant sur moins d'éléments.
Une Skip List est une alternative intéressante à certaines structures
arborescentes. Cette structure de données associatives de type {clé, valeur}
n'admet qu'une valeur par clé, conserve les valeurs en ordre croissant de clé, et
offre plusieurs caractéristiques intéressantes sur le plan de la complexité
algorithmique.
Les Skip List sont très utiles lorsque vous devez pouvoir accéder
simultanément à votre structure de données. Dans une liste à ignorer, si vous
devez insérer un nouveau nœud, seuls les nœuds adjacents seront affectés, de
sorte que vous pouvez toujours accéder à une grande partie de vos données
pendant que cela se produit.

Figure 2 : Skip List

 L'idée générale va comme suit :


- Une Skip List est une liste de nœuds à plusieurs niveaux, au sens où
chaque nœud possède une paire {clé, valeur}et au moins un pointeur sur
un nœud suivant.
- Le niveau maximal (nombre maximal de suivants par nœud) est choisi à
la construction, mais le nombre de suivants d'un nœud est déterminé de
manière pseudo aléatoire.
- Le niveau zéro de chaque nœud mène au nœud suivant. Si nous
parcourions une Skip List strictement à partir des suivants de niveau zéro,
alors la Skip List serait essentiellement une liste simplement chaînée avec
un coût en espace pour tenir compte des autres niveaux.
- Les autres niveaux mènent vers un nœud « en avant » un peu plus loin.
Plus le niveau est haut et plus épars sont les suivants. Conséquemment,
parcourir une Skip List à partir de suivants de niveau près du niveau
maximum escamote plusieurs nœuds à la fois.
- Le parcours d'une Skip List suivra d'abord des suivants de haut niveau,
puis des nœuds de plus bas niveau, jusqu'à parcourir des nœuds de niveau
zéro dans le pire cas. Ceci correspond à escamoter d'abord les nœuds par
grands « blocs », puis par blocs progressivement plus petits, jusqu'à ce
que la clé recherchée soit trouvée, ou jusqu'à ce qu'il soit déterminé
qu'elle n'est pas dans la Skip List.

2 Les algorithmes
Une skip-List a besoin d’avoir un pointeur vers sa tête. La liste doit connaitre
le nombre de niveaux actuellement utilisés (level). Selon les implémentations, il
est possible de définir la hauteur maximale (levelMAX).
Il est aussi possible de changer le paramètre (p) de probabilité de création d’une
nouvelle couche. La variation de ces deux derniers paramètres est étudiée dans
le chapitre Analyse des performances. Un nœud contient une clé key, une valeur
value ainsi qu’un tableau de pointeur vers les nœuds suivants forward. Il n’est
pas nécessaire d’enregistrer la hauteur d’un nœud dans celui-ci.

typedef struct SkipList {


int levelMAX, //Size
int level;
struct node *header;
} SkipList ;
typedef struct node {
int key ;
int value ;
struct node *forward;
} node;

L’initiation se fait de la manière suivante :


- La hauteur maximale est déterminée par le nombre d’éléments totaux
(n) présents dans la liste.
- Création d’un élément NIL possédant une clé plus grande que le maxi-
mum autorisé. Cet élément est aussi appelé élément bidon.
- Définition de ses pointeurs forward vers lui-même.
- Le nombre de voies actuelles est fixé à 1.
- Le pointeur de tête est défini par cet élément NIL.
La création d’une Skip-List en langage C figure en annexe.

Figure 3 : Initialisation d’un Skip List


SkipList *Initialisation (SkipList *list)
{ int i ;
node *header = (node*)malloc(sizeof(struct node)) ;
liste -> header = header ;
header -> key = INT_MAX ;
header -> forward=(node*)malloc(sizeof(node*)*(SKIPLIST_MAX_LEVEL+1)) ;
for ( i = 0 ; i <= SKIPLIST_MAX_LEVEL ; i++)
{ header -> forward[i] = list -> header ;}
list -> level = 1 ;
list -> size = 0 ;
return list ;
}

2.1 La recherche

De manière générale, pour rechercher dans une Skip List il suffit de commencer
par le niveau le plus haut et d’effectuer une recherche élément par élément. Si
l’élément suivant devient plus grand que celui recherché, il suffit alors de des-
cendre d’un niveau dans la liste.
Algorithmique parlant, la recherche d’un élément s’exécute de la façon suivante:
 On se place dans la première clé de la liste du haut
 Tant qu’on peut descendre et qu’on n’a pas trouvé l’élément de clé k, on
exécute les opérations suivantes :
- On descend.
- Tant que la clé à droite est plus petite que k, on se déplace vers la droite.

Exemple : chercher un élément de clé 78 :

Figure 4 : chercher un élément


node *SkipList_search(SkipList *list, int key)
{ snode *x = list->header;
int i;
for (i = list->level; i >= 1; i--) {
while (x->forward[i]->key < key)
x = x->forward[i];
}
if (x->forward[1]->key == key)
{ return x->forward[1];}
else
{ return NULL;}
return NULL;
}

2.2 L’Insertion

L’insertion d’une entrée dans une skip List est essentiellement la même
chose que la recherche. À l’exception qu’un tableau update, de taille identique à
la hauteur de la liste, est utilisé pour mémoriser les pointeurs de chaque élément
avant de descendre d’un niveau. Ce tableau est utilisé pour mettre à jour les liens
nécessaires pour que la structure de données reste cohérente.
Algorithmiquement parlant, l’insertion d’un élément dans une skip-List s’ef-
fectue de la façon suivante :
1. Commencer l’exploration de la liste par l’élément en tête, et se position-
ner sur la plus haute voie.
2. On joue à « pile ou face » jusqu’à ce que l’on obtienne « pile ». On dé-
note i le nombre de fois où on a eu « face » avant « pile ».
3. Si i >= h, on ajoute à la Skip Liste de nouvelles séquences « niveau ».
Chacune contenant seulement les éléments de clés spéciales +∞ et -∞.
Exemple : insérer un élément de clé 15, i=2 :
- On utilise l’algorithme chercher pour trouver les positions p des entrées
de plus grande clé plus petite ou égale à k dans chaque séquence S.
- Pour j = 0,….,i, on insère (k,v) dans Sj après la position Pj.
Figure : Insertion d’un élément
sxs
int skiplist_insert(skiplist *list, int key, int value)
{ snode *update[SKIPLIST_MAX_LEVEL+1];
snode *x = list->header; int i, level;
for (i = list->level; i >= 1; i--)
{ while (x->forward[i]->key < key)
x = x->forward[i];
update[i] = x; }
x = x->forward[1];
if (key == x->key)
{ x->value = value;
return 0;
} else {
level = rand_level();
if (level > list->level) {
for (i = list->level+1; i <= level; i++)
{ update[i] = list->header; }
list->level = level; }
x = (snode *)malloc(sizeof(snode));
x->key = key;
x->value = value;
x->forward = (snode **)malloc(sizeof(snode*) * (level + 1));
for (i = 1; i <= level; i++) {
x->forward[i] = update[i]->forward[i];
update[i]->forward[i] = x;}}
return 0;}
2.3 La suppression
La suppression d’un élément dans une skip-List s’effectue de la façon suivante :
1. On cherche dans la Skip List et on trouve les positions P d’une entrée
de clé k, ou la position Pj est dans le niveau Sj.
2. On enlève les éléments de positions p des niveaux S
3. On ne garde qu’une séquence ne contenant que les deux clés spéciales.
Exemple : Enlever 34 :

Figure : La suppression d’un élément


,
int skiplist_delete(skiplist *list, int key)
{ int i;
snode *update[SKIPLIST_MAX_LEVEL + 1];
snode *x = list->header;
for (i = list->level; i >= 1; i--) {
while (x->forward[i]->key < key)
x = x->forward[i];
update[i] = x; }
x = x->forward[1];
if (x->key == key) {
for (i = 1; i <= list->level; i++) {
if (update[i]->forward[i] != x) break;
update[i]->forward[1] = x->forward[i]; }
skiplist_node_free(x);
while (list->level > 1 && list->header->forward[list->level] == list->header)
list->level--;
return 0; }
return 1;
}
2.4 La hauteur

La notion de hauteur est très importante dans une skip-List. En effet,


celle-ci permet de définir à combien de nœuds un élément est attaché. Si trop
d’éléments possèdent la même hauteur, la recherche devient trop lente.
Cette hauteur est définie à l’aide d’une fonction aléatoire. Celle-ci ne dépend ni
de la clé de l’élément ni de sa valeur. Dans le cas où p = 1/2, la fonction aléa-
toire doit être définie pour qu’un élément de hauteur h possède le double de pro-
babilités qu’un élément de hauteur h+1. Ainsi, si un nœud de hauteur 1 a une
probabilité p de 0.5 alors le niveau 2 a une probabilité de 0.25.

3 La complexité
Scénario Attendu Pire
Rechercher O(log n) O(n)
Insertion O(log n) O(n)
Suppression O(log n) O(n)

Vous aimerez peut-être aussi