Estructuras de datos en memoria principal Franco Guidi Polanco Escuela de Ingeniería Industrial Pontificia Universidad Católica de Valparaíso, Chile fguidi@ucv.cl Estructuras de datos v Estructuras básicas § Arreglo § Lista enlazada • Simplemente enlazada • Doblemente enlazada v Colecciones implementadas sobre las estructuras básicas: § § § § § § § Lista, Lista con iterador Lista circular Pila Cola Hashtable Vector (Java) (Otras) Franco Guidi Polanco (PUCV-EII) 25-05-14 2 Arreglo v Es una colección ordenada de elementos del mismo tipo. v Es de largo fijo, definido al momento de instanciarlo. v El acceso a los elementos se hace a través de un subíndice. v Fácil de recorrer en ambos sentidos. v Estudiado en cursos anteriores Franco Guidi Polanco (PUCV-EII) 25-05-14 3 Listas enlazadas v Son estructuras dinámicas: se asigna memoria para los elementos de la lista en la medida que es necesario. v Cada elemento se almacena en una variable dinámica denominada nodo. v En la lista simplemente enlazada, cada nodo apunta al nodo que contiene el elemento siguiente Franco Guidi Polanco (PUCV-EII) 25-05-14 4 Esquema tentativo de una lista simplemente enlazada Nodos data data head data null ListaEnlazada Franco Guidi Polanco (PUCV-EII) data 25-05-14 5 Datos contenidos en la lista v Los nodos de una lista contendrán datos del tipo declarado en la estructura del nodo. Por ejemplo: § Tipos primitivos (byte, int, boolean, char, etc.) § Referencias a objetos v En los siguientes ejemplos consideraremos el uso de listas de enteros, aunque las técnicas que serán descritas son aplicables a cualquier otro “tipo” de lista. Franco Guidi Polanco (PUCV-EII) 25-05-14 6 Diagrama de clases de una lista simplemente enlazada v Diagrama de clases (lista de enteros): next Lista 1..1 head agregarAlFinal(d:Data) estáContenido(d:Data):boolean eliminar(d:Data):boolean imprimirContenido() Nodo data:int getData():int setNext(n:Nodo) getNext():Nodo Nodo(d:Data,n:Nodo) v Diagrama de objetos: :Lista head:Nodo data = 1 Franco Guidi Polanco (PUCV-EII) 25-05-14 :Nodo :Nodo data = 20 data = -1 7 Una lista simplemente enlazada (versión preliminar) v Declaración de la Lista: public class Lista{ Nodo head = null; public void agregarAlFinal(int data){ ... } public void imprimirContenido(){ ... } public boolean estaContenido(int data){ ... } public boolean eliminar(int data){ ... } } Franco Guidi Polanco (PUCV-EII) 25-05-14 8 Nodos en una lista simplemente enlazada public class Nodo{ private int data; private Nodo next; public Nodo(int d, Nodo n){ data = d; next = n; } public int getData(){ return data; } public Nodo getNext(){ return next; } public void setNext(Nodo n){ next = n; } } Franco Guidi Polanco (PUCV-EII) 25-05-14 9 Inserción en la lista simplemente enlazada v Insertar elementos: Ejemplo: [ 25, 1, 14, 4 ] § Al final de la lista 25 1 14 4 head null § Manteniendo un orden 1 4 14 25 head null § Al inicio de la lista 4 14 head Franco Guidi Polanco (PUCV-EII) 1 25 null 25-05-14 10 Inserción de elementos al final de la lista v Caso 1: la lista está vacía (variable head contiene null) 25 head head null null public Lista{ ... public void agregarAlFinal(int dato){ ... head = new Nodo( dato, null ); ... } ... } Franco Guidi Polanco (PUCV-EII) 25-05-14 11 Inserción de elementos al final de la lista v Caso 2 (general): la lista tiene al menos un elemento 25 1. ubicar último nodo de la lista (aquél cuya variable next contiene null) 2. Instanciar el nuevo nodo con el contenido indicado 3. Asignar el nuevo nodo a la variable next del último nodo (asegurándose de que la variable next del nuevo nodo sea igual a null) 1 head null aux 25 14 1 head null aux Franco Guidi Polanco (PUCV-EII) aux.setNext(new Nodo(dato, null)); 25-05-14 12 Inserción de elementos al final de la lista v Caso general: public class Lista{ ... public void agregarAlFinal(int dato){ Nodo nuevo = new Nodo(dato, null); if( head == null ) head = nuevo; else{ Nodo aux = head; while( aux.getNext() != null) aux = aux.getNext(); aux.setNext( nuevo ); } } ... } Franco Guidi Polanco (PUCV-EII) 25-05-14 13 Recorrido de la lista v Método que imprime el contenido de la lista: public class Lista{ ... public void imprimirContenido(){ Nodo aux = head; while( aux != null ){ System.out.print( aux.getData() + "; " ); aux = aux.getNext(); } System.out.println(); ... } Franco Guidi Polanco (PUCV-EII) 25-05-14 14 Búsqueda en la lista v Retorna true si el elemento está contenido en la lista public class Lista{ ... public boolean estaContenido(int data){ Nodo aux = head; while( aux != null ){ if( data == aux.getData() ) return true; aux = aux.getNext(); } return false; } ... } Franco Guidi Polanco (PUCV-EII) 25-05-14 15 Eliminación de un elemento v Requiere identificar el nodo a borrar. v Caso 1: es el primer nodo de la lista 25 1 14 4 head null head = head.getNext(); 25 head 1 4 Sin otras referencias: candidato a eliminación (recolector de basura de Java) 1 14 head Franco Guidi Polanco (PUCV-EII) 14 null 4 null 25-05-14 16 Eliminación de un elemento v Caso 2 (general): no es el primer nodo de la lista 25 1 14 4 head null aux.setNext(aux.getNext().getNext()); aux 25 head 1 4 Sin otras referencias: candidato a eliminación (recolector de basura de Java) 25 1 head Franco Guidi Polanco (PUCV-EII) 14 null 4 null 25-05-14 17 Eliminación de un elemento public class Lista{ ... public boolean eliminar(int data){ if( head != null) if( head.getData() == data ){ head = head.getNext(); return true; }else{ Nodo aux = head; while( aux.getNext() != null ){ if( aux.getNext().getData() == data ){ aux.setNext( aux.getNext().getNext() ); return true; } aux = aux.getNext(); } } return false; } ... } Franco Guidi Polanco (PUCV-EII) 25-05-14 18 Simplificación del esquema propuesto: uso de un nodo “fantasma” v En el esquema propuesto se deben hacer excepciones al insertar y eliminar el nodo del comienzo de la lista. v El manejo se simplifica si se utiliza un nodo “fantasma”: § Es un nodo siempre presente en la lista § Su contenido es irrelevante (el valor u objeto contenido no forma parte de la lista). Franco Guidi Polanco (PUCV-EII) 25-05-14 19 Lista simplemente enlazada con nodo fantasma v Lista vacía: head public class Lista{ Nodo head; public Lista(){ head = new Nodo(0, null); } ... null v Lista con elementos: } Valor irrelevante 25 1 4 head null Primer elemento de la lista Franco Guidi Polanco (PUCV-EII) 25-05-14 20 Operación de inserción en la lista con nodo fantasma v La inserción del primer elemento de la lista entra en el caso general: 1 head null aux Clase Lista aux.setNext(new Nodo(dato, null)); public void agregarAlFinal(int dato){ Nodo aux = head; while( aux.getNext() != null) aux = aux.getNext(); aux.setNext( new Nodo(dato, null) ); } } Franco Guidi Polanco (PUCV-EII) 25-05-14 21 Eliminación del primer elemento en la lista con nodo fantasma v La eliminación del primer elemento de la lista entra en el caso general: aux aux.setNext(aux.getNext().getNext()); 14 1 4 head null Sin otras referencias: candidato a eliminación (recolector de basura de Java) Franco Guidi Polanco (PUCV-EII) 25-05-14 22 Eliminación del primer elemento en la lista con nodo fantasma (cont.) public boolean eliminar(int data){ Nodo aux = head; while( aux.getNext() != null ){ if( aux.getNext().getData() == data ){ aux.setNext( aux.getNext().getNext() ); return true; } aux = aux.getNext(); } return false; } Franco Guidi Polanco (PUCV-EII) 25-05-14 23 Mejora al procedimiento de inserción de elementos al final de la lista v El procedimiento descrito anteriormente requiere que todas las veces sea encontrado el último elemento de la lista. v Más conveniente: tener una variable de instancia que siempre referencie al último elemento de la lista. v Esto aplica a listas con o sin nodo fantasma (con pequeños cambios). Franco Guidi Polanco (PUCV-EII) 25-05-14 24 Mejora al procedimiento de inserción de elementos al final de la lista (cont.) v Diagrama de clases: Lista 1..1 agregarAlFinal(d:Data) estáContenido(d:Data) eliminar(d:Data):boolean imprimirContenido() head 1..1 1..1 tail Nodo data: int getData():Data setNext(n:Nodo) getNext():Nodo Nodo(d:Data,n:Nodo) v Diagrama de objetos: Lista head ghost:Nodo 1stNode:Nodo 2ndNode:Nodo data = 20 data = -1 tail Franco Guidi Polanco (PUCV-EII) 25-05-14 25 Mejora al procedimiento de inserción de elementos al final de la lista (cont.) v La variable de instancia tail mantiene siempre la referencia al último elemento: head null public class Lista{ Nodo head, tail; public Lista(){ head = new Nodo(0, null); tail = head; } tail ... } 25 head 1 4 null tail Franco Guidi Polanco (PUCV-EII) 25-05-14 26 Mejora al procedimiento de inserción de elementos al final de la lista (cont.) v El método agregarAlFinal ya no requiere recorrer la lista para ubicar el último nodo: public void agregarAlFinal(int dato){ Nodo aux = new Nodo(dato, null) ; tail.setNext( aux ); tail = aux; Versión con nodo fantasma } v La varaible tail es actualizada después de la inserción. v Notar que el procedimiento de eliminación debe actualizar la referencia tail si se remueve el último nodo de la lista. Franco Guidi Polanco (PUCV-EII) 25-05-14 27 Inserción en orden/al inicio v Ejercicio 1: implemente el método: agregarEnOrden(int dato) que recibe un entero y lo agrega en orden ascendente a la lista. v Ejercicio 2: implemente el método agregarAlInicio(int dato) que recibe un entero y lo agrega como primer elemento. Franco Guidi Polanco (PUCV-EII) 25-05-14 28 Resumen listas simplemente enlazadas v Útiles para guardar un número no predefinido de elementos. v Distintas disciplinas para mantener los datos ordenados (y para removerlos). v El acceso a los nodos es secuencial; el recorrido es en una sola dirección (Ejercicio: confrontar con arreglos) Franco Guidi Polanco (PUCV-EII) 25-05-14 29 Listas doblemente enlazadas v Están diseñadas para un acceso fácil al nodo siguiente y al anterior. v Cada nodo contiene dos referencias: una apuntando al nodo siguiente, y otra apuntando al nodo anterior. v El acceso a los nodos sigue siendo secuencial. v La técnica del nodo fantasma puede ser útil también en este tipo de lista. 1 14 head 4 null null Franco Guidi Polanco (PUCV-EII) 25-05-14 30 Comentarios finales sobre estructuras elementales v Estar abierto a definir y utilizar otras estructuras. v Ejemplo: Lista simplemente enlazada y circular 25 1 14 4 head Franco Guidi Polanco (PUCV-EII) 25-05-14 31 Colecciones implementadas sobre estructuras básicas Franco Guidi Polanco Escuela de Ingeniería Industrial Pontificia Universidad Católica de Valparaíso, Chile fguidi@ucv.cl Interfaces versus implementación v En la sección anterior estudiamos estructuras elementales para implementar colecciones. v En esta sección estudiaremos colecciones clásicas, desde dos perspectivas: § La interfaz de la colección (cómo se utiliza) § La implementación de la colección (cómo se construye) v Una misma interfaz puede ser soportada por múltiples implementaciones v La eficiencia en la operación de una colección va a depender de su implementación Franco Guidi Polanco (PUCV-EII) 25-05-14 33 Listas v Representa una colección conformada por una secuencia finita y ordenada de datos denominados elementos. v Ordenada implica que cada elemento tiene una posición. v Los elementos corresponden a un tipo de dato. v La lista está vacía cuando no contiene elementos. v El número de elementos se denomina largo de la lista. v El comienzo de la lista es llamado cabeza (head), y el final cola (tail). v Notación: (a1, a2, ..., an). Franco Guidi Polanco (PUCV-EII) 25-05-14 34 Lista: versión clásica v Además de los datos, el estado de la lista contiene una identificación (referencia) al “dato actual”. (12, 22, 50, 30). v La lista provee operaciones para: § § § § § § Cambiar (modificar la referencia) del dato actual Retornar el dato actual Eliminar el dato actual Modificar el dato actual Insertar un dato en la posición del dato actual Borrar toda la lista, buscar elementos en la lista, contar sus elementos Franco Guidi Polanco (PUCV-EII) 25-05-14 35 Interfaces para la versión clásica de la lista v No hay una única definición formal de interfaz. v Estudiaremos dos extremos: § Interfaz elemental § Interfaz extendida Franco Guidi Polanco (PUCV-EII) 25-05-14 36 Interfaz elemental para la lista clásica public interface List { public void clear(); // public void insert(Object item); // public Object remove(); // public void setFirst(); // public void next(); // public int length(); // public void setValue(Object val);// public Object currValue(); // public boolean isEmpty(); // public boolean eol(); // public String toString(); // } Elimina todos los elem. Inserta elem. en act. Saca/retorna elem. Setea act. en 1ra pos. Mueve act. a sig. pos. Retorna largo Setea elemento Retorna elemento True: lista vacía True: act en end of list Retorna lista de elem. Nota: pos.=posición; act.=posición actual; sig.:siguiente; prev.:previa Franco Guidi Polanco (PUCV-EII) 25-05-14 37 Ejemplo de uso de una lista (versión clásica) v Sea la siguiente lista: v Operaciones: miLista=(12, 22, 50, 30) Dato actual § miLista.insert(99) : miLista=(12, 99, 22, 50, 30) § miLista.next(): Dato actual miLista=(12, 99, 22, 50, 30) Dato actual Franco Guidi Polanco (PUCV-EII) 25-05-14 38 Ejemplo de uso de una lista (versión clásica) § miLista.currValue(): miLista=(12, 99, 22, 50, 30) Dato actual 22 § miLista.remove(): miLista=(12, 99, 50, 30) (Nuevo) dato actual 22 § miLista.setFirst(): miLista=(12, 99, 50, 30) Dato actual Franco Guidi Polanco (PUCV-EII) 25-05-14 39 Interfaz extendida para la lista clásica public interface ExtendedList { public void clear(); // public void insert(Object item); // public void append(Object item); // public Object remove(); // public void setFirst(); // public void next(); // public void prev(); // public int length(); // public void setPos(int pos); // public void setValue(Object val);// public Object currValue(); // public boolean isEmpty(); // public boolean eol(); // public String toString(); // } Elimina todos los elem. Inserta elem. en act. Agrega elem. al final Saca/retorna elem. Setea act. en 1ra pos. Mueve act. a sig. pos. Mueve act. a pos. prev. Retorna largo Setea act. a pos Setea elemento Retorna elemento True: lista vacía True: act en end of list Retorna lista de elem. Nota: pos.=posición; act.=posición actual; sig.:siguiente; prev.:previa Franco Guidi Polanco (PUCV-EII) 25-05-14 40 Implementación de la lista clásica v La conveniencia de una u otra implementación depende de las operaciones definidas en la interfaz. v Interfaz elemental: § Implementación basada en lista simplemente enlazada § Implementación basada en arreglos § Implementación basada en lista doblemente enlazada (sobredimensionada) v Interfaz extendida: § Implementación basada en arreglos § Implementación basada en lista doblemente enlazada § Implementación basada en lista simplemente enlazada Franco Guidi Polanco (PUCV-EII) 25-05-14 41 Implementación de la lista clásica basada en arreglos v Usa un arreglo para almacenar los elementos de la lista. v Los elementos son almacenados en posiciones contiguas en el arreglo. v El elemento “i” de la lista se almacena en la celda “i-1” del arreglo. v La cabeza de la lista siempre está en la primera posición del arreglo (0). v El máximo número de elementos en la lista se define al crear el arreglo. Franco Guidi Polanco (PUCV-EII) 25-05-14 42 Inserción en lista basada en arreglos Insertar 23 13 12 20 0 1 2 8 3 3 4 5 0 (a) 13 12 20 8 3 1 4 5 2 3 (b) 23 13 12 20 8 3 0 4 5 1 2 3 (c) Franco Guidi Polanco (PUCV-EII) 25-05-14 43 Comparación entre implementaciones de listas v Las listas basadas en arreglos tienen la desventaja de que su número de elementos debe ser predeterminado. v Cuando estas listas tienen pocos elementos, se desperdicia espacio. v Las listas enlazadas no tienen límite de número máximo de elementos (mientras la memoria lo permita). Franco Guidi Polanco (PUCV-EII) 25-05-14 44 Comparación entre implementaciones de listas v Las listas basadas en arreglos son más rápidas que aquellas basadas en listas enlazadas para el acceso aleatorio por posición. v Las operaciones de inserción y eliminación son más rápidas en las listas enlazadas. v En general, si el número de elementos que contendrá una lista es muy variable o desconocido, es mejor usar listas enlazadas. Franco Guidi Polanco (PUCV-EII) 25-05-14 45 Uso de listas v Supongamos las siguientes implementaciones de la lista: § LList, implementa List mediante una lista simplemente enlazada § AList, implementa List mediante un arreglo v Referenciamos la lista por medio de su interfaz: ... List lista = new LList(); // Implementación seleccionada lista.insert( “Hola” ); lista.insert( “chao” ); lista.setFirst(); while( !lista.eol() ){ System.out.println( (Sring)lista.currValue()); lista.next(); } lista.setFirst(); String eliminado = (String) lista.remove() ... Franco Guidi Polanco (PUCV-EII) 25-05-14 46 Uso de listas (cont.) v Si en el programa anterior ahora se desea utilizar otra implementación de lista, sólo debe cambiarse la clase a instanciar. ... List lista = new AList(); // Implementación seleccionada lista.insert( “Hola” ); lista.insert( “chao” ); lista.setFirst(); while( !lista.eol() ){ System.out.println( (Sring)lista.currValue()); lista.next(); } lista.setFirst(); String eliminado = (String) lista.remove() ... Franco Guidi Polanco (PUCV-EII) 25-05-14 47 Pilas v Es un tipo restringido de lista, en donde los elementos sólo pueden ser insertados o removidos desde un extremo, llamado top. v Se le llama también Stack o Lista LIFO (Last In, First Out). v La operación para insertar un nuevo elemento se denomina push. v La operación para remover un elemento se denomina pop. Franco Guidi Polanco (PUCV-EII) 25-05-14 48 Interfaz para la Pila public interface Stack { public void clear(); // Remueve todos los objetos public void push(Object it); // Agrega objeto al tope public Object pop(); // Saca objeto del tope public Object topValue();// Retorna objeto en el tope public boolean isEmpty(); // True: pila vacía } Franco Guidi Polanco (PUCV-EII) 25-05-14 49 Ejemplo de uso de una pila v pila.push(4): 4 v pila.push(14): top v pila.push(1): 1 14 4 Franco Guidi Polanco (PUCV-EII) top 14 4 v pila.pop(): 1 top 14 4 25-05-14 top (nuevo) 50 Implementación de una pila mediante lista simplemente enlazada top null pila.push(4) 4 top pila.push(14) 14 null Se inserta y remueve siempre el primer elemento: no se requiere nodo fantasma 4 top null pila.pop() 4 top Franco Guidi Polanco (PUCV-EII) null 25-05-14 51 Implementación de una pila mediante un arreglo top=4 3 5 8 2 0 1 2 3 4 5 v En esta clase, por conveniencia, se usa una variable top, que siempre está 1 posición más adelante del elemento “superior” de la pila. Franco Guidi Polanco (PUCV-EII) 25-05-14 52 Ejercicio pilas v Proponga implementaciones de la pila: § Basada en un arreglo § Basada en una lista simplemente enlazada Franco Guidi Polanco (PUCV-EII) 25-05-14 53 Colas v Es un tipo restringido de lista, en donde los elementos sólo pueden ser agregados al final, y removidos por el frente. v Se le llama también Queue o Lista FIFO (First In, First Out). v La operación para agregar un nuevo elemento se denomina enqueue. v La operación para remover un elemento se denomina dequeue. Franco Guidi Polanco (PUCV-EII) 25-05-14 54 Interfaz para la Cola public interface Queue { public void clear(); // Remueve todos los objetos public void enqueue(Object it);// Agrega obj. al final public Object dequeue(); // Saca objeto del frente public Object firstValue(); // Retorna obj. del frente public boolean isEmpty(); // Retorna V si cola vacía } Franco Guidi Polanco (PUCV-EII) 25-05-14 55 Ejemplo de uso de una cola v cola.enqueue( 20 ) cola=(20) v cola.enqueue( 15 ) cola=(20, 15) v cola.enqueue( 40 ) cola=(20, 15, 40) v cola.dequeue() cola=(15, 40) v cola.dequeue() 20 cola=(40) 15 Franco Guidi Polanco (PUCV-EII) 25-05-14 56 Cola basada en lista simplemente enlazada 25 1 1 4 null front rear v Es conveniente mantener una referencia al último elemento de la cola (facilita operación enqueue). Franco Guidi Polanco (PUCV-EII) 25-05-14 57 Cola basada en arreglos (cont.) v Aproximación simple: almacenar los “n” elementos de la cola en las “n” primeras posiciones del arreglo. rear=3 3 5 Sacar del frente: 5 8 2 rear=2 8 2 Problema: lentitud de procedimiento dequeue (sacar primer elemento). Franco Guidi Polanco (PUCV-EII) 25-05-14 58 Cola basada en arreglos (cont.) v Aproximación mejorada: al hacer dequeue, no desplazar elementos, sino asumir que el frente de la cola se desplaza. front=0 3 5 8 rear=3 2 Sacar del frente: front=1 rear=3 5 8 2 Problema: Al sacar y agregar elementos, rear llega a la última posición del arreglo y la cola no puede crecer, aun cuando existan posiciones libres al comienzo. Franco Guidi Polanco (PUCV-EII) 25-05-14 59 Cola basada en arreglos* (cont.) v Arreglo circular: Pretender que el arreglo es “circular”, y permitir que la cola continúe directamente de la última posición del arreglo a la primera. 7 front=0 0 3 6 1 5 8 5 2 4 Franco Guidi Polanco (PUCV-EII) 3 2 rear=3 25-05-14 Función de avance Dados: • pos: posición actual • size: tamaño arreglo pos=(pos+1); if (pos>=size) pos=0; 60 Cola basada en arreglos* (cont.) v Problema de arreglo circular: § Si front es igual a rear, implica que hay 1 elemento. § Luego, rear está una posición detrás de front implica que la cola está vacía. § Pero si la cola está llena, rear también está una posición detrás de front. v Problema: ¿Cómo reconocer cuando una cola está vacía o llena? v Solución: Usar un arreglo de n posiciones, para almacenar como máximo n-1 elementos. Franco Guidi Polanco (PUCV-EII) 25-05-14 61 Cola basada en arreglos* (cont.) v Por conveniencia, se usa una variable front para apuntar a la posición precedente al elemento frontal. v La cola está vacía cuando front=rear. v La cola está llena cuando rear está justo detrás de front, o sea cuando la función de avance indica que de aumentar rear en 1, se llegaría a front. Franco Guidi Polanco (PUCV-EII) 25-05-14 62