Está en la página 1de 20

Sesin

U V A UN NI I VE ER RS SI ID DA AD DC CA AT T L LI IC CA AD DE ES SA AN NT TA AM MA AR RI I A
FACULTAD DE CIENCIAS E INGENIERA

INGENIERA DE SISTEMAS
LISTAS SKIP

LABORATORIO DE ALGORITMOS Y ESTRUCTURA DE DATOS II

I OBJETIVOS Explicar el TAD lista skip. Implementar el TAD lista skip. Utilizar el TAD lista skip. para la resolucin de problemas. II TEMAS TAD lista skip Insertar en lista skip Eliminar en lista skip III MARCO TERICO LISTA SKIP (LISTA CON SALTOS) Lista ordenada (y en principio sin claves repetidas) en la que cada nodo puede tener uno o varios enlaces al nodo siguiente y a diversos nodos a distancias variables, bien establecidas de forma aleatoria o siguiendo un patrn establecido de saltos a longitudes fijas. Son estructuras de datos probabilistas. Son una alternativa a los rboles de bsqueda equilibrados (AVL, 2-3, rojinegros,) para almacenar diccionarios de n datos con un coste promedio O(log n) de las operaciones. Son muchos ms fciles de implementar que, por ejemplo, los rboles AVL o los rojinegros. Lista enlazada (ordenada):

El coste de una bsqueda en el caso peor es lineal en el nmero de nodos. Aadiendo un puntero a cada nodo par

Ahora el nmero de nodos examinados en una bsqueda es, como mucho Y aadiendo otro a cada nodo mltiplo de 4

N /2

1.

Mgter. Juan Pablo Apaza Condori

Ahora el nmero de nodos examinados en una bsqueda es, como mucho,

N /4

Y por fin el caso lmite: cada nodo mltiplo de (para todo

2i apunta al nodo que est 2i

lugares por delante

0 ):

El nmero total de punteros se ha duplicado (con respecto a la lista enlazada inicial). Ahora el tiempo de una bsqueda est acotado superiormente por log 2 n , porque la bsqueda consiste en avanzar al siguiente nodo (por el puntero alto) o bajar al nivel siguiente de punteros y seguir avanzando En esencia, se trata de una bsqueda binaria. Problema: la insercin es demasiado difcil. En particular

Si llamamos nodo de nivel k al que tiene k punteros, se cumple la siguiente propiedad (ms dbil que la definicin del caso lmite): El puntero i-simo de cualquier nodo de nivel k (k nivel i o superior. i) apunta al siguiente nodo de

Adoptamos sta como definicin de lista skip (junto con la decisin aleatoria del nivel de un nuevo nodo).

Mgter. Juan Pablo Apaza Condori

LISTA SKIP: EJEMPLO

LISTA SKIP: BSQUEDA Se empieza en el puntero ms alto de la cabecera. Se contina por ese nivel hasta encontrar un nodo con clave mayor que la buscada (o NIL), entonces se desciende un nivel y se contina de igual forma. Cuando el avance se detiene en el nivel 1, se est frente el nodo con la clave buscada o tal clave no existe en la lista. algoritmo buscar(lista:lista_skip; clave_buscada:clave) principio x:=lista.cabecera; {invariante: x .clave<clave_buscada} para i:=lista.nivel descendiendo hasta 1 hacer mq x .sig[i] .clave<clave_buscada hacer x:=x .sig[i] fmq fpara; {x .clave<clave_buscada x .sig[1] .clave} x:=x .sig[1]; si x .clave=clave_buscada ent devuelve x .valor sino fracaso en la bsqueda fsi fin
boolean buscar(register list l, register TipoClave clave, TipoValor *valorPointer) { register int k; register pnodo p,q; p = l->header; k = l->nivel; do { while (q = p->forward[k], q->clave < clave) p = q; k--; } while (k>=0); if (q->clave != clave) return(false); *valorPointer = q->valor; //escribe por referencia el valor asociado a la clave return(true); }

Mgter. Juan Pablo Apaza Condori

EJEMPLO Buscando el 27

PROPUESTOS Busque el 74

Mgter. Juan Pablo Apaza Condori

LISTA SKIP: INSERCIN Primero: para insertar un nuevo elemento hay que decidir de qu nivel debe ser. En una lista de las del caso lmite la mitad de los nodos son de nivel 1, una cuarta parte de nivel 2 y, en general, 1/2i nodos son de nivel i. Se elige el nivel de un nuevo nodo de acuerdo con esas probabilidades: se tira una moneda al aire hasta que salga cara, y se elige el nmero total de lanzamientos realizados como nivel del nodo. Distribucin geomtrica de parmetro p = 1/2. (En realidad se puede plantear con un parmetro arbitrario, p, y luego seleccionar qu valor de p es ms adecuado) Segundo: hay que saber dnde insertar. Se hace igual que en la bsqueda, guardando traza de los nodos en los que se desciende de nivel.

Se determina aleatoriamente el nivel del nuevo nodo y se inserta, enlazando los punteros convenientemente.

algoritmo inserta(lista:lista_skip; c:clave; v:valor) variable traza:vector[1..MaxNivel] de punteros principio x:=lista.cabecera; para i:=lista.nivel descendiendo hasta 1 hacer mq x .sig[i] .clave<c hacer x:=x .sig[i] fmq; {x .clave<c x .sig[i] .clave} traza[i]:=x fpara; x:=x .sig[1]; si x .clave=c ent x .valor:=v sino {insercin} nivel:=nivel_aleatorio;

Mgter. Juan Pablo Apaza Condori

si nivel>lista.nivel ent para i:=lista.nivel+1 hasta nivel hacer traza[i]:=lista.cabecera fpara; lista.nivel:=nivel fsi; x:=nuevoDato(nivel,c,v); para i:=1 hasta nivel hacer x .sig[i]:=traza[i] .sig[i]; traza[i] .sig[i]:=x fpara fsi fin
EJEMPLO Insertar el 29

PROPUESTOS Insertar lo siguiente: 30, 5, 18, 25, 50, 34 DESCARTE Es el proceso inverso a la insercin. Se busca el nodo que ser descartado, almacenado en el arreglo auxiliar, las direcciones de los punteros a nodos con claves mayores a las del que ser descartado. Si lo encuentra, copia los punteros hacia delante (los rojos), en los nodos con claves menores al buscado y que fueros marcados en al bsqueda, en las casillas en que salen vnculos azules. Luego de liberar el espacio ocupado por el nodo, es preciso revisar, a partir del la lista de mayor nivel antes de descartar, si qued vaca para ajustar, disminuyendo en uno, el nivel mximo de la lista de saltos.

Mgter. Juan Pablo Apaza Condori

LISTA SKIP: OBSERVACIONES Se requiere una estimacin a priori del tamao de la lista (igual que en tablas hash) para determinar el nmero de niveles. Si no se dispone de esa estimacin, se puede asumir un nmero grande o usar una tcnica similar al rehashing (reconstruccin). Resultados experimentales muestran que las listas skip son tan eficientes como muchas implementaciones de rboles de bsqueda equilibrados, y son ms fciles de implementar. LISTA SKIP: ELIMINACIN Los pasos para realizar la eliminacin son: 1 2 3 4 Localizar el item con clave K para ser eliminado Sino existe el elemento, retornar no existe la clave K De otra manera eliminar el item K Eliminar tambin todos los tems que se encuentran encima.

EJEMPLO Eliminar 18

SOLUCIN

PROPUESTOS Eliminar 74, 12, 72, 74

Mgter. Juan Pablo Apaza Condori

IV ACTIVIDADES Ejecutar y evaluar la implementacin de listas skip. LISTA SKIP 01: IMPLEMENTACIN EN C/C++ Pruebe el siguiente programa de lista skip: ListaSkip.h #include "ListaSkip.h" int mrandom() { return( (int) rand()); } //se usa el generador aleatorio de la biblioteca. //Inicio de variables globales. void init(void) { NIL = newNodeOfLevel(0); NIL->clave = INT_MAX; //0x7fffffff; //para enteros de 32 bits NIL->valor = 666; //cualquier valor. Se inicia pero no se usa. randomBits = mrandom(); //inicia variables del generador. randomsLeft = BitsInRandom/2; srand(1); } //Creador y destructor. list newList(void) { list sl; int i; sl = (list)malloc(sizeof(struct listStructure)); sl->nivel = 0; sl->header = newNodeOfLevel(MaxNumberOfLevels); sl->header->clave=-INT_MAX; //la deja en menos infinito. sl->header->valor=0; //No es necesario iniciarlas ya que no se usan los valores del header. for(i=0; i<MaxNumberOfLevels; i++) sl->header->forward[i] = NIL; return(sl);

} void freeList(list sl) { register pnodo p,q; p = sl->header; do { q = p->forward[0]; //printf("%d \n",p->clave); free(p); //borra los nodos del nivel k=0. //el primero que borra es el nodo header. p = q; } while (p!=NIL); free(sl); //solo queda por liberar el nodo centinela. } //Listador.

Mgter. Juan Pablo Apaza Condori

void prtList(register list l) { register int k,cnt,punteros,acum; register pnodo p,q; int claves[MaxNumberOfLevels]; k = l->nivel; punteros=0; do { p = l->header; //imprime nodos de nivel k if(k>=0 && p->forward[k]!=NIL) printf("k=%d ->", k); cnt=0; while (q = p->forward[k], q->clave !=INT_MAX) { p = q; cnt++; punteros++; } if(cnt==0) printf("Lista vaca\n"); else printf("N=%d \n", cnt); //muestra el nmero de nodos en el nivel k claves[k]=cnt; k--; } while (k>=0); if (cnt>0) { for(acum=0, k=l->nivel;k>=0;k--) { acum=claves[k]; } //imprime nodos en cada nivel putchar('\n'); } if(punteros) printf("Punteros por clave=%f \n", (float) punteros/(float) cnt ); } //Genera nivel aleatorio. //Esta es la rutina que aleatoriza el algoritmo. int randomLevel(void) { register int nivel = 0; register int b; do { b = randomBits&Mascara; //inspecciona ltimos nbits if (!b) nivel++; //en (1<<nbits)-1 de (1<<nbits) casos incrementa nivel randomBits>>=nbits; randomsLeft--; if (randomsLeft == 0) // Cuando se acaban, invoca al generador { randomBits = mrandom(); //recarga nmero aleatorio randomsLeft = BitsInRandom/nbits; //reinicia pasadas } }while (!b);

Mgter. Juan Pablo Apaza Condori

return(nivel>MaxLevel ? MaxLevel : nivel); } //Insertar, descartar. #ifdef allowDuplicates void insertar(register list l, register TipoClave clave, register TipoValor valor) #else boolean insertar(register list l, register TipoClave clave, register TipoValor valor) #endif { register int k; register pnodo p, q; p = l->header; k = l->nivel; do { while (q = p->forward[k], q->clave < clave) { p = q; //operador coma. } update[k] = p; //salva enlaces hacia al nuevo multinodo k--; }while(k>=0); #ifndef allowDuplicates if (q->clave == clave) { q->valor = valor; //sobreescribe valor. return(false); } #endif k = randomLevel(); //printf("*%d*\n",k); if (k > l->nivel) //si es mayor slo aumenta al nivel siguiente { k = ++l->nivel; update[k] = l->header;// hay que actualizar el nuevo nivel } q = newNodeOfLevel(k); q->clave = clave; q->valor = valor; do { p = update[k]; //Inserta en todas las listas a partir de la k-sima //printf(":%d:\n",k); q->forward[k] = p->forward[k]; //pega q con los proximos p->forward[k] = q; //enlaza q con los anteriores k--; } while(k>=0); #ifndef allowDuplicates return(true); #endif

} boolean descartar(register list l, register TipoClave clave)

Mgter. Juan Pablo Apaza Condori

10

} //Buscar. boolean buscar(register list l, register TipoClave clave, TipoValor *valorPointer) { register int k; register pnodo p,q; p = l->header; k = l->nivel; do { while (q = p->forward[k], q->clave < clave) p = q; k--; } while (k>=0); if (q->clave != clave) return(false); *valorPointer = q->valor; //escribe por referencia el valor asociado a la clave return(true); }

register int k,m; register pnodo p,q; p = l->header; k = m = l->nivel; do { while (q = p->forward[k], q->clave < clave) { p = q; } update[k] = p; k--; } while(k>=0); if (q->clave == clave) { for(k=0; k<=m && (p=update[k])->forward[k] == q; k++) p->forward[k] = q->forward[k]; //liga las listas free(q); if( l->header->forward[m] == NIL && m > 0 ) m--; //si lista vaca en nivel m (es if no while.) l->nivel = m; //fija el nivel return(true); } else return(false); //si no encontr la clave

Mgter. Juan Pablo Apaza Condori

11

ListaSkip.c #include <stdio.h> #include <stdlib.h> #include <time.h> #define false 0 #define true 1 typedef char boolean; #define MaxNumberOfLevels 16 //sirve hasta para log(n)=16 => n=2^16 #define MaxLevel (MaxNumberOfLevels-1) #define newNodeOfLevel(k) (pnodo)malloc(sizeof(nodo)+(k)*sizeof(pnodo )) typedef int TipoClave; typedef int TipoValor; typedef struct nodeStructure { TipoClave clave; TipoValor valor; //otro valor almacenado en el nodo. struct nodeStructure *forward[1]; /* arreglo de punteros de largo variable */ } nodo, *pnodo ; //La estructura siguiente agrupa el nivel de la lista con un puntero al header de la lista de saltos. typedef struct listStructure { int nivel; /* Niveles de la lista */ pnodo header; /* Apunta al nodo de encabezado que contiene MaxNumberOfLevels punteros */ } * list; //Variables globales. pnodo NIL; //apunta a nodo centinela #define BitsInRandom 15 //31 para enteros de 32 #define nbits 2 #define Mascara (1<<nbits)-1 int randomsLeft; //variables para el generador aleatorio de niveles. int randomBits; pnodo update[MaxNumberOfLevels]; //arreglo global para mantener los enlaces

Mgter. Juan Pablo Apaza Condori

12

LISTA SKIP 02: IMPLEMENTACIN EN C/C++ Pruebe el siguiente programa de lista skip: DSL.h #ifndef DSL_H_ #define DSL_H_ #include "dsexceptions.h" #include <iostream> using namespace std; // // // // // // // // // // // // // // Deterministic skip list class class CONSTRUCTION: with INFINITY object that is also used to signal failed finds ******************PUBLIC OPERATIONS********************* void insert( x ) --> Insert x void remove( x ) --> Remove x (unimplemented) Comparable find( x ) --> Return item that matches x Comparable findMin( ) --> Return smallest item Comparable findMax( ) --> Return largest item boolean isEmpty( ) --> Return true if empty; else false void makeEmpty( ) --> Remove all items void printList( ) --> Print items in sorted order // For NULL

// Node and forward declaration because g++ does // not understand nested classes. template <class Comparable> class DSL; template <class Comparable> class SkipNode { Comparable element; SkipNode *right; SkipNode *down; SkipNode( const Comparable & theElement = Comparable( ), SkipNode *rt = NULL, SkipNode *dt = NULL ) : element( theElement ), right( rt ), down( dt ) { } }; friend class DSL<Comparable>;

template <class Comparable> class DSL { public: explicit DSL( const Comparable & inf );

Mgter. Juan Pablo Apaza Condori

13

DSL( const DSL & rhs ); ~DSL( ); const Comparable & findMin( ) const; const Comparable & findMax( ) const; const Comparable & find( const Comparable & x ) const; bool isEmpty( ) const; void printList( ) const; void makeEmpty( ); void insert( const Comparable & x ); void remove( const Comparable & x ); const DSL & operator=( const DSL & rhs ); private: const Comparable INFINITY; SkipNode<Comparable> *header; // The list SkipNode<Comparable> *bottom; SkipNode<Comparable> *tail; const Comparable & elementAt( SkipNode<Comparable> * t ) const; // Usual recursive stuff void reclaimMemory( SkipNode<Comparable> *t ) const; }; #include "DSL.cpp" #endif

Mgter. Juan Pablo Apaza Condori

14

DSL.cpp #include "DSL.h" /** * Construct the tree. * inf is the largest Comparable * and is used to signal failed finds. */ template <class Comparable> DSL<Comparable>::DSL( const Comparable & inf ) : INFINITY( inf ) { bottom = new SkipNode<Comparable>( ); bottom->right = bottom->down = bottom; tail = new SkipNode<Comparable>( INFINITY ); tail->right = tail; header = new SkipNode<Comparable>( INFINITY, tail, bottom ); } /** * Copy constructor. * Left as an exercise. */ template <class Comparable> DSL<Comparable>::DSL( const DSL<Comparable> & rhs ) : INFINITY( rhs.INFINITY) { cout << "Copy constructor is unimplemented" << endl; } /** * Destructor. */ template <class Comparable> DSL<Comparable>::~DSL( ) { makeEmpty( ); delete header; delete tail; delete bottom; } /** * Insert item x into the DSL. */ template <class Comparable> void DSL<Comparable>::insert( const Comparable & x ) { SkipNode<Comparable> *current = header; bottom->element = x; while( current != bottom ) { while( current->element < x ) current = current->right;

Mgter. Juan Pablo Apaza Condori

15

// If gap size is 3 or at bottom level and // must insert, then promote middle element if( current->down->right->right->element < current->element ) { current->right = new SkipNode<Comparable>( current->element, current->right, current->down->right->right ); current->element = current->down->right->element; } else current = current->down; } // Raise height of DSL if necessary if( header->right != tail ) header = new SkipNode<Comparable>( INFINITY, tail, header );

/** * Remove item x from the DSL. Unimplemented. */ template <class Comparable> void DSL<Comparable>::remove( const Comparable & x ) { cout << "Sorry, remove unimplemented; " << x << " still present" << endl; } /** * Find the smallest item in the tree. * Return smallest item or INFINITY if empty. */ template <class Comparable> const Comparable & DSL<Comparable>::findMin( ) const { if( isEmpty( ) ) return INFINITY; SkipNode<Comparable> *current = header; while( current->down != bottom ) current = current->down; } return elementAt( current );

/** * Find the largest item in the tree. * Return the largest item or INFINITY if empty. */ template <class Comparable> const Comparable & DSL<Comparable>::findMax( ) const { if( isEmpty( ) ) return INFINITY; SkipNode<Comparable> *current = header;

Mgter. Juan Pablo Apaza Condori

16

for( ; ; ) if( current->right->right != tail ) current = current->right; else if( current->down != bottom ) current = current->down; else return elementAt( current ); } /** * Find item x in the tree. * Return the matching item or INFINITY if not found. */ template <class Comparable> const Comparable & DSL<Comparable>::find( const Comparable & x ) const { SkipNode<Comparable> *current = header; bottom->element = x; for( ; ; ) if( x < current->element ) { cout<<"|"<<current->element; current = current->down; } else if( current->element < x ) { cout<<"--"<<current->element; current = current->right; } else return elementAt( current ); } /** * Make the tree logically empty. */ template <class Comparable> void DSL<Comparable>::makeEmpty( ) { reclaimMemory( header ); header->right = tail; header->down = bottom; } /** * Test if the tree is logically empty. * Return true if empty, false otherwise. */ template <class Comparable> bool DSL<Comparable>::isEmpty( ) const { return header->right == tail && header->down == bottom; }

Mgter. Juan Pablo Apaza Condori

17

/** * Internal method to get element field from node t. * Return the element field or INFINITY if t is at the bottom. */ template <class Comparable> const Comparable & DSL<Comparable>:: elementAt( SkipNode<Comparable> *t ) const { if( t == bottom ) return INFINITY; else return t->element; } /** * Print the DSL. */ template <class Comparable> void DSL<Comparable>::printList( ) const { SkipNode<Comparable> *current = header; while( current->down != bottom ) { } while( current->right != tail ) { cout << current->element << endl; current = current->right; } //cout << current->element << endl; current = current->down;

/** * Deep copy. Left as an exercise */ template <class Comparable> const DSL<Comparable> & DSL<Comparable>::operator=( const DSL<Comparable> & rhs ) { if( this != &rhs ) cout << "Sorry, operator= is unimplemented" << endl; return *this; } /** * reclaimMemory is left as an exercise. * Hint: delete from top level to bottom level. */ template <class Comparable> void DSL<Comparable>::reclaimMemory( SkipNode<Comparable> *t ) const

Mgter. Juan Pablo Apaza Condori

18

{ }

if( t != bottom ) cout << "reclaimMemory is unimplemented -- leaking!" << endl;

Mgter. Juan Pablo Apaza Condori

19

V EJERCICIOS 1 Modifique el programa de Listas Skip para mostrar el siguiente resultado.

2 Modifique el programa de Listas Skip para mostrar el siguiente resultado.

3 Modifique el programa para que genere 4 o ms niveles con valores menores a 100. 4 Agregue al programa de listas skip la opcin de mostrar todos los nodos de un determinado nivel. 5 Agregue al programa de listas skip la opcin de mostrar todos los nodos del nivel ms bajo y del ms alto.

Mgter. Juan Pablo Apaza Condori

20

También podría gustarte