Resumen Listas Enlazadas Estructuras Dinámicas de Datos Las estructuras dinámicas de datos son estructuras que crecen a medida que se ejecuta un programa. Al contrario de un arreglo que contiene un espacio de memoria determinado para almacenar un número fijo de elementos, asignado al inicio de un programa, una estructura dinámica de datos se amplia y contrae durante la ejecución de un programa según sea la necesidad de agregar o eliminar elementos. Una estructura dinámica de datos es una colección de elementos llamados nodos que están conectados entre sí por enlaces de apuntador (referencias a objetos). Un nodo (Figura 1) es una estructura de datos que contiene dos campos, un campo que almacena el dato (dato primitivo u objeto) y otro campo denominado enlace que almacena la referencia al próximo nodo. dato enlace Figura 1. Estructura de un nodo Implementación de una clase Nodo Con campo de dato primitivo int Con campo de dato genérico Object public class Nodo { public class Nodo { public int dato; public Nodo enlace; public Object dato; public Nodo enlace; public Nodo(int d) { dato = d; enlace = null; } public Nodo(int d, Nodo e) { dato = d; enlace = e; } public Nodo(Object d) { dato = d; enlace = null; } public Nodo(Object d, Nodo e) { dato = d; enlace = e; } } } Listas enlazadas Una lista enlazada (Figura 2) es un conjunto lineal de objetos de una clase llamada Nodo. Una lista enlazada se accesa por medio de una referencia al primer nodo de la lista, generalmente llamado cab. Los nodos subsiguientes se accesan por medio del campo de enlace que esta almacenado en cada nodo. Por convención el enlace del último nodo de la lista se establece a null para marcar el final de la lista. cab Referencia al null dato enlace dato enlace dato enlace primer nodo Figura 2. Lista enlazada Universidad de Carabobo. Facultad de Ingeniería. Departamento de Computación. Computación Avanzada. 1-2006. Listas Enlazadas 2 En una lista enlazada, los datos se almacenan dinámicamente y cada nodo se crea conforme se necesita. Las listas de datos pueden almacenarse en arreglos, pero las listas enlazadas proporcionan varias ventajas. Una lista enlazada es adecuada cuando es impredecible el número de datos a almacenar en la estructura. Las listas enlazadas pueden mantenerse de forma ordenada simplemente insertando cada nuevo dato en el punto adecuado de la lista sin necesidad de mover los elementos existentes en la lista. Las listas enlazadas tienen la desventaja que por cada dato de la lista requiere de un espacio de memoria para el enlace al próximo dato, adicionalmente la asignación dinámica de memoria incurre en la sobrecarga de llamadas a función a diferencia del acceso a un elemento de un arreglo, que se realiza directamente calculando la posición del elemento con respecto a la dirección de inicio del arreglo. Clasificación Las listas enlazadas se clasifican en: Listas enlazadas simples, doblemente enlazadas, circulares simplemente enlazadas y circulares doblemente enlazadas. Implementación de una clase Lista public class Lista { private Nodo cab; public Lista() { cab = null; } public Nodo getCab(){ return cab; } public boolean vacia(){ return cab == null; } public String toString(){ String s = ""; Nodo aux = cab; while (aux != null){ s+= aux.dato+"\t"; aux = aux.enlace; } return s; } public void visualizar(){ System.out.println("Lista\n"+this); } Operaciones básicas en una lista enlazada: Insertar un nodo en la cabecera de la lista public Lista insertarCab(int d){ cab = new Nodo(d,cab); return this; } Se crea un nuevo nodo con el enlace apuntando al elemento apuntado por cab (el primero de la lista). A cab se le asigna la referencia al nuevo nodo creado. Universidad de Carabobo. Facultad de Ingeniería. Departamento de Computación. Computación Avanzada. 1-2006. Listas Enlazadas Determinar la referencia al último nodo de la lista /* Busca y retorna la referencia al último nodo de la lista, si la lista esta vacía retorna null*/ public Nodo ultimoNodo(){ Nodo aux = cab; Nodo ultimo = null; while (aux != null){ ultimo = aux; aux = aux.enlace; } return ultimo; } aux se utiliza para recorrer la lista y ultimo se utiliza para guardar la referencia al nodo anterior al referenciado por aux. Retorna null si la lista esta vacía. Insertar un nuevo nodo al final de la lista. /* Inserta un nodo al final de la lista */ public Lista insertarFinal(int d){ if (vacia()) insertarCab(d); else{ Nodo ultimo = ultimoNodo(); ultimo.enlace = new Nodo(d); } return this; } 3 Si la lista esta vacía inserta el dato d en la cabecera de la lista. Sino crea un nuevo nodo con enlace a null y lo enlaza con el último nodo de la lista. Accesar un dato almacenado en un nodo específico. /* Busca y retorna la referencia al nodo que almacena un valor d */ public Nodo buscarDato(int d) throws Exception{ if (vacia()) throw new Exception("Lista vacía"); else{ Nodo aux = cab; while((aux != null) && (aux.dato!= d)) aux = aux.enlace; return aux; } } Si la lista esta vacía lanza una excepción. aux se utiliza para recorrer la lista e ir accesando cada campo dato para compararlo con d. Sino encuentra el dato d, retorna null. Universidad de Carabobo. Facultad de Ingeniería. Departamento de Computación. Computación Avanzada. 1-2006. Listas Enlazadas 4 Eliminar un nodo existente que contiene una información en particular. /* Elimina el nodo cuyo dato sea igual a un valor d que recibe como parámetro*/ public Lista eliminarDato(int d) throws Exception{ if (vacia()) throw new Exception("Lista vacía"); else{ Nodo b = buscarDato(d); if (b != null){ Nodo a = nodoAnterior(d); if (a == null) cab = cab.enlace; else a.enlace = b.enlace; } return this; } } Si la lista esta vacía lanza una excepción. a se utiliza para almacenar la referencia al nodo anterior y b para almacenar la referencia al nodo que almacena d. Si d no existe (b == null) no hace nada. Si encuentra el dato d, determina la referencia al nodo anterior a d, si la referencia al anterior es null significa que d esta en el primer nodo de la lista. Obtener la referencia al nodo anterior a un nodo que almacena un valor d. /* Busca y retorna la referencia al nodo anterior al nodo que almacena un valor d*/ public Nodo nodoAnterior(int d) throws Exception{ if (vacia()) throw new Exception("Lista vacía"); else{ Nodo anterior = null; Nodo aux = cab; while((aux != null) && (aux.dato!= d)){ anterior = aux; aux = aux.enlace; } return anterior; } } Si la lista esta vacía lanza una excepción. aux se utiliza para recorrer la lista y anterior se utiliza para guardar la referencia al nodo anterior al referenciado por aux. Si d se encuentra en el primer nodo retorna null. Si d no existe retorna la referencia al último nodo de la lista. Este método se puede modificar para que retorne una excepción cuando d no se encuentre en la lista. public Nodo nodoAnterior(int d) throws Exception{ if (vacia()) throw new Exception("Lista vacía"); else{ if (buscarDato(d)==null) throw new Exception(d + "no encontrado"); else{ Nodo anterior = null; Nodo aux = cab; while((aux != null) && (aux.dato!= d)){ anterior = aux; aux = aux.enlace; } return anterior; } }} De este modo si retorna null, es porque el dato d esta en el primer nodo de la lista. Sino, retorna la referencia del nodo anterior a d. Universidad de Carabobo. Facultad de Ingeniería. Departamento de Computación. Computación Avanzada. 1-2006. Listas Enlazadas 5 Insertar un dato v en una posición anterior a un nodo que almacena un dato d. /* Inserta un nodo con dato v antes de un nodo cuyo dato sea igual a un valor d que recibe como parámetro*/ public Lista insertarAntes(int v, int d) throws Exception{ if (vacia()) insertarCab(v); else{ Nodo b = buscarDato(d); if (b != null){ Nodo a = nodoAnterior(d); if (a == null) insertarCab(v); else a.enlace = new Nodo(v,a.enlace); } } return this; } a se utiliza para almacenar la referencia al nodo anterior y b para almacenar la referencia al nodo que almacena d. Si la lista esta vacía, inserta v en la cabecera. Sino busca determina el valor de b. Si lo encuentra, determina el valor de a. Si a==null, es porque d es el primero de la lista, entones lo inserta en la cabecera. Sino crea un nuevo nodo y lo enlaza. Si d no existe no inserta v. Insertar un dato d manteniendo la lista ordenada ascendentemente. /* * Inserta un dato d manteniendo la lista ordenada * en orden ascendente * si la lista esta vacía, lo inserta en la cabecera */ public Lista insertarOrd(int d){ if (vacia()) insertarCab(d); else{ Nodo aux = cab; Nodo anterior = null; while((aux != null) && (d > aux.dato)){ anterior = aux; aux = aux.enlace; } if (anterior == null) insertarCab(d); else anterior.enlace = new Nodo(d,anterior.enlace); } return this; } Si la lista está vacía inserta d en la cabecera. Sino determina la posición donde debe insertar d para mantener el orden. Para ello, determina la referencia al nodo anterior a donde debe insertar. Si d es menor que el dato del primer nodo (anterior == null) inserta d en la cabecera. Sino crea un nuevo nodo con d y lo enlaza Universidad de Carabobo. Facultad de Ingeniería. Departamento de Computación. Computación Avanzada. 1-2006. Listas Enlazadas 6 Pilas Una pila (stack) es un tipo especial de una lista enlazada, ya que los nuevos nodos sólo pueden agregarse o eliminarse por la cabecera de la lista, generalmente llamado tope o cima de la pila. Se caracteriza por ser una estructura LIFO (último en entrar primero en salir, Last Input First Output). Implementación de una clase Pila import listasEnlazadas.*; //para usar Nodo de int public class Pila { private Nodo tope; public Pila() { tope = null; } public Nodo getTope(){ return tope; } Solo se permite el acceso al elemento ubicado en el tope de la Pila tanto para eliminarlo (pop) como para agregarlo (push). La referencia al primer Nodo es tope. public boolean vacia(){ return tope == null; } /** agrega un dato en el tope de la pila*/ public Pila push(int d){ tope = new Nodo(d, tope); return this; } /** retira el dato del tope de la pila*/ public Pila pop() throws Exception{ if (vacia()) throw new Exception("Pila vacía"); else tope = tope.enlace; return this; } /** retorna el dato ubicado en el tope de la pila*/ public int info() throws Exception{ if (vacia()) throw new Exception("Pila vacía"); else return tope.dato; } public String toString(){ String s = ""; Nodo aux = tope; while (aux != null){ s+= aux.dato + "\n"; aux = aux.enlace; } return s; }} Universidad de Carabobo. Facultad de Ingeniería. Departamento de Computación. Computación Avanzada. 1-2006. Listas Enlazadas 7 Colas Una cola (queue) es similar a una cola de personas para comprar entradas al cine, la primera persona que está en la cola, es la primera en ser atendida, las nuevas personas que lleguen deben agregarse al final de la cola. En una cola implementada con una lista enlazada, los nodos de una cola se eliminan solamente desde el inicio de la lista y se insertan solamente al final de la lista. Se caracteriza por ser una estructura FIFO (primero en entrar primero en salir, First Input First Output). Implementación de una clase Cola import listasEnlazadas.*; //para usar Nodo de int public class Cola { private Nodo primero; private Nodo ultimo; public Cola() { primero = null; ultimo = null; } public boolean vacia(){ return primero == null; } /* agrega un elemento al final de la cola */ public Cola entrar(int d){ if (vacia()){ primero = new Nodo(d); ultimo = primero; }else{ ultimo.enlace = new Nodo(d); ultimo = ultimo.enlace; } return this; } Se utilizan dos apuntadores: primero que referencia el primer nodo de la lista y facilita las operaciones de acceso al primer elemento de la cola. ultimo que referencia al ultimo nodo de la lista y facilita las operaciones de acceso al ultimo elemento de la cola. Es muy importante mantener actualizados estos apuntadores, cada vez que se agregan o extraen elementos de una Cola. /* elimina un elemento ubicado al inicio de la cola*/ public Cola salir(int d) throws Exception{ if (vacia()) throw new Exception("Cola vacía"); else{ primero = primero.enlace; if (primero == null) ultimo = null; } return this; } public String toString(){ String s = ""; Nodo aux = primero; while (aux != null){ s+= aux.dato + "\t"; aux = aux.enlace; } return s; } } Universidad de Carabobo. Facultad de Ingeniería. Departamento de Computación. Computación Avanzada. 1-2006. Listas Enlazadas 8 Ejercicios propuestos 1. Desarrollar un programa que cree una lista enlazada de 10 caracteres y luego cree un segundo objeto de lista, que contenga una copia de la primera lista, pero en orden inverso. 2. Escriba un programa que dada una línea de texto utilice un objeto de pila para imprimir la pila en orden inverso. 3. Agregar un método concatenar a la clase Lista para concatenar dos Listas. public Lista concatenar(Lista obj) Por ejemplo: Lista b Lista a cab 10 12 14 null cab 100 Lista concatenada 200 null cab 10 12 14 100 200 null 4. Agregar un método a la clase Lista que modifique el campo dato del n-ésimo nodo de una lista enlazada por un valor dado x. 5. Agregar un método a la clase Lista que realice la inserción contigua a la izquierda del n-ésimo nodo de una lista enlazada y un método que realice la inserción contigua a la derecha del n-ésimo nodo. 6. Dados un conjunto de puntos pertenecientes a un plano. Desarrollar un programa que cree una lista de objetos Point y obtenga en una nueva Lista la distancia existente entre cada punto (Point) de la lista y el primer punto (Point) almacenado en la lista. El primer nodo de la nueva Lista almacenará cero, porque es la distancia con respecto a sí mismo. Utilice la clase Lista del package listaGenerica y utilice el método distance de la clase Point para calcular la distancia entre puntos. 7. Se tiene almacenada cada palabra de un texto en una lista enlazada. Determinar la frecuencia de aparición de cada una de las palabras en el texto. Por ejemplo, para el texto: los libros los llevo mañana los compro mañana. Se tiene que la frecuencia de aparición de cada palabra es: los 3 nnn libros 1 nnn llevo 1 nnn mañana 2 nnn compro 1 null Universidad de Carabobo. Facultad de Ingeniería. Departamento de Computación. Computación Avanzada. 1-2006.