Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante...

34
Estructuras de Datos Implementación de Listas

Transcript of Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante...

Page 1: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

Estructuras de Datos

Implementación de Listas

Page 2: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

1. Mediante arreglos de tamaño fijo2. Mediante arreglos de tamaño variable3. Mediante listas ligadas

En todos los casos los elementos de la lista son de tipo stdelement (elementos con campo clave que los hace únicos)

Alternativas

Page 3: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

Alternativas 1 y 2:

• En cada posición del arreglo se guarda un elemento de tipo stdelement

• En el caso del arreglo estático se define (reserva) un número de elementos suficiente desde el inicio del programa

• En el caso del arreglo dinámico, por medio de las función realloc() se puede expandir (debido

a una inserción) y contraer (debido a un borrado) el arreglo a medida que se requiera

Page 4: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

• Ambas alternativas implican que se realicen “corrimientos” de elementos tanto en inserciones como en borrados

• Las operaciones de navegación con el cursor son bastante simples (se utiliza el índice del arreglo)

• Nos concentraremos en la alternativa 3 aunque a continuación se verán los fundamentos de la alternativa 2

Page 5: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

Función Realloc()

• Es una potente función para el manejo de memoria dinámica que permite crear y manipular arreglos dinámicos (arreglos que cambian de tamaño durante la ejecución), su sintaxis es:

puntero = (tipo_de_variable *) realloc( direccion_de_mem, numero_de_bytes );

• Está en stdlib.h

Page 6: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

• dirección_de_mem es la dirección del espacio de memoria cuyo tamaño se desea modificar, si dirección_de_mem es nulo, realloc() reservará un nuevo espacio de memoria

• El argumento numero_de_bytes es el nuevo tamaño que se desea asignar al bloque referenciado por dirección_de_mem. Si numero_de_bytes es 0 (cero) este bloque es liberado.

• realloc() devuelve la dirección del espacio de memoria ya contraído o expandido Esta dirección puede ser diferente a dirección_mem ¿por qué?

Page 7: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

Veamos un ejemplo:

int *cambiar_tamano( int *arreglo, int n ){

int *r;r = (int *)realloc(arreglo, n*sizeof(int));return r;

}

Dirección inicial Número de elementosdeseados

Page 8: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

void main(){

int *arreglo; //Se crea

arreglo = cambiar_tamano( NULL, 5 ); arreglo[2] = 4;

//Se expandearreglo = cambiar_tamano( arreglo, 10 );

// Se destruye arreglo = cambiar_tamano( arreglo, 0 );}

Page 9: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

Supóngase que se va a manejar una lista de estudiantes:

class estudiante{public: int aCarne;public: estudiante(); // Constructor por defecto estudiante(int carne);};

estudiante::estudiante(){ aCarne = 0;}

estudiante::estudiante(int carne){ aCarne = carne;}

Page 10: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

class lista{public: estudiante *primero; // Puntero para el manejo del vector que

// representa la lista de estudiantes int n; // Número de estudiantes int cursor; // Posición del elemento actualpublic:

//Constructorlista();//Destructoravoid destruir_lista(lista *l);//Inserciónvoid inserte_antes(estudiante e);

};

Page 11: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

void lista::destruir_lista(lista *l){ if(n == 0) delete l; else cout<<"La lista debe ser vacia";}

lista::lista(){ primero = NULL; n = 0; cursor = 0;}

Page 12: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

void lista::inserte_antes(estudiante e){ int i; primero=(estudiante*)realloc((primero),

(n+1)*sizeof(estudiante)); if(n == 0) { primero[0] = e; cursor++; // El cursor queda valiendo 1 pero se ocupa la // posición 0 } else { for(i=0; i<=(n-cursor); i++){ primero[(n-i)] = primero[((n-1)-i)]; } primero[cursor-1] = e; }n++;}

Page 13: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

void main(){ estudiante e1(100); lista milista;

cout << milista.n << milista.cursor; milista.inserte_antes(e1); cout<< milista.primero[0].aCarne; estudiante e2(200); milista.inserte_antes(e2); cout<< milista.primero[0].aCarne; cout<< milista.primero[1].aCarne;}

Page 14: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

Alternativa 3

• Las listas pueden ser:

–Simplemente ligadas–Simples circulares–Doblemente ligadas–Dobles circulares–Todas las anteriores y con registro cabeza

• Nos concentraremos en las listas doblemente ligadas no circulares sin registro cabeza

Page 15: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

• En la implementación doblemente ligada, cada elemento de la lista está representado por un nodo

• Un nodo contiene, además del stdelement que se desea almacenar en la lista un par de punteros, uno de ellos apunta al siguiente nodo de la lista y el otro al nodo anterior

• Una lista doblemente enlazada es un conjunto de nodos conectados entre sí

• Cada nodo tiene asociado una dirección de memoria

• Los nodos no necesariamente tienen que estar en posiciones contiguas de memoria

Page 16: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

NULL NULLe2 e3e1

Una lista doblemente enlazada de estudiantes:e1,e2, e3 son estudiantes

[43] [81][76]

[76] [43] [81] [76]

Direcciones de los nodos

Page 17: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

El operador flecha ->#include <iostream.h>

struct persona{ int ced;

};

void main(void){ struct persona p1,*p2;

p1.ced=4;p2 = &p1;cout<< p2->ced;cout<< (*p2).ced;

}

Page 18: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

La clase nodoclass nodo{ public: nodo *siguiente; nodo *anterior; estudiante e; public: //Constructor nodo(nodo *sig, nodo *ant, estudiante e1);};

//Implementación Constructornodo::nodo(nodo *sig, nodo *ant, estudiante e1){siguiente=sig;anterior=ant;e=e1;}

Page 19: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

class lista{ public: nodo *primero; // Apunta al primer elemento de la

lista nodo *cursor; // Apunta a un nodo de la lista int n; // Número de elementos de la listapublic://Constructor lista();//Destructora void destruir_lista(lista *l);//Construcción void inserte_antes(estudiante e);};

Page 20: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

lista::lista(){ primero = NULL; n = 0; cursor = NULL;}

void lista::destruir_lista(lista *l){ if(l->cursor == NULL) delete l; else cout<<"Error: la lista debe ser vacia";}

Page 21: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

void lista::inserte_antes(estudiante e){nodo *nnodo;nnodo = new nodo(NULL, NULL, e); int cmb_primero = 0;

if(cursor !=NULL && cursor->anterior != NULL)//Si hay anterior

{ nnodo->anterior=cursor->anterior; (cursor->anterior)->siguiente=nnodo;}else //Ya sea que cursor = NULL o que cursor esté en el primero{ nnodo->anterior=NULL; cmb_primero=1; //Activa bandera para primero}

CONTINUA

Page 22: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

if(primero != NULL) //Si la lista era no vacía{ nnodo->siguiente=cursor; cursor->anterior=nnodo;}else //Si la lista era vacía{ nnodo->siguiente=NULL; cmb_primero=1;}if( cmb_primero){ primero=nnodo; //Cambio de primero} cursor=nnodo; //El cursor queda en el actual n++; //Incrementa el número de elementos}

Page 23: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

void main(){ estudiante e1(100); lista milista;

cout << milista.n <<endl; milista.inserte_antes(e1); cout << (milista.primero)->e.aCarne<<endl; cout << (milista.cursor)->e.aCarne<<endl; cout << milista.n << endl; estudiante e2(200); milista.inserte_antes(e2); cout << (milista.primero)->e.aCarne<<endl; cout << (milista.cursor)->e.aCarne<<endl; cout << milista.n<<endl; }

Imprime: 0 100 100 1 200 200 2

Page 24: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

//Veamos otro main() que hace uso de la lista anterior:void main(){ estudiante eaux; lista milista; int carnet, n;

cout<< "Nro de estudiantes a ingresar: "; cin >> n; for(int k=1; k<=n; k++) { cout<<"Carnet: " << endl; cin>> carnet; eaux.aCarne = carnet; milista.inserte_antes(eaux); } El main()

Continúa

Page 25: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

//Recorrer la lista desde el primero hasta el final: nodo *ptnodo; ptnodo = milista.primero; while(ptnodo != NULL) {

cout << ptnodo->e.aCarne << endl;ptnodo = ptnodo ->siguiente;

}/*Este ciclo imprime los datos en el orden

inverso a la entrada...*/}

Fin del main()

Page 26: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

Ahora se va a añadir a la clase lista la función ultimo():Se agrega a la clase la siguiente función:

nodo *ultimo();

Y la implementación correspondiente es:

nodo *lista::ultimo(){

nodo *ult=NULL,*aux=primero;//aux también podría iniciarse en el cursor...while (aux != NULL){ ult = aux; aux = aux ->siguiente;}return ult;

}

Page 27: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

• Ahora se puede recorrer la lista desde el último hasta el primero así:

cout<<"Del último al primero"<<endl; ptnodo = milista.ultimo(); while(ptnodo != NULL) {

cout << ptnodo->e.aCarne << endl;ptnodo = ptnodo->anterior;

}

Page 28: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

Se pueden crear igualmente funciones recursivas para el recorrido por ejemplo:

void imprimir1(nodo *ptnodo){

if (ptnodo != NULL ){

cout << ptnodo ->e.aCarne << endl;imprimir1(ptnodo ->siguiente);

}}

Page 29: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

void imprimir2(nodo *ptnodo){

if (ptnodo != NULL ){

imprimir2(ptnodo ->siguiente);cout << ptnodo ->e.aCarne << endl;

}}

Page 30: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

Desde el main() se pueden invocar así:

void main(){ estudiante eaux; lista milista; int carnet, n;

cout<< "Nro de estudiantes a ingresar: "; cin >> n; for(int k=1; k<=n; k++) { cout<<"Carnet: " << endl; cin>> carnet; eaux.aCarne = carnet; milista.inserte_antes(eaux); } imprimir1(milista.primero); imprimir2(milista.primero);}

Page 31: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

Ahora agregará a la lista la función existe(k), se asumirá el carnet como la clave. Pero primero se agrega a la clase estudiante la función clave() así:

class estudiante{ public: int aCarne; public: estudiante(); estudiante(int carne); int clave();};

int estudiante::clave(){ return aCarne; }

Clave

Page 32: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

Ahora se agrega a la clase lista la función existe(k) así: int existe(int k);

Y la implementación es:lista::existe(int k){

nodo *pt=primero;while(pt != NULL){

if(pt ->e.clave() == k ){return 1;

} //verdadero, encontradopt=pt->siguiente;

}return 0; // no encontrado

}

Page 33: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

Agregar a la clase la siguiente función y su correspondiente implementación:

lista::existe(int k){

nodo *pt=primero;while(pt != NULL){

if(pt ->e.clave() == k ){return 1; //Verdadero, encontrado

}pt=pt->siguiente;

}return 0; // No encontrado

}

Page 34: Estructuras de Datos Implementación de Listas. 1. Mediante arreglos de tamaño fijo 2. Mediante arreglos de tamaño variable 3. Mediante listas ligadas.

El main() para ejemplificar la invocación es:

void main(){ estudiante eaux; lista milista; int carnet, n;

cout<< "Nro de estudiantes a ingresar: "; cin >> n; for(int k=1; k<=n; k++) { cout<<"Carnet: " << endl; cin>> carnet; eaux.aCarne = carnet; milista.inserte_antes(eaux); }cout << milista.existe(3);cout << milista.existe(8);}