.jpg) Listas Enlazadas, Pilas y Colas
    
    Listas Enlazadas, Pilas y Colas
  Tabla de contenidos
.png) 1. 
      Sesión 4 (laboratorio): Pilas y Colas
1. 
      Sesión 4 (laboratorio): Pilas y Colas
      
     1.1. 
		Implementación de una pila con listas enlazadas
1.1. 
		Implementación de una pila con listas enlazadas
		
Mediante la resolución de este ejercicio se pretende que el alumno aprenda a implementar una pila utilizando listas enlazadas como estructura de datos.
Implementa una pila utilizando una lista enlazada como estructura de datos. La pila debe permitir almacenar cualquier tipo de objeto, e implementar al menos métodos para:
- Insertar un objeto en la pila (push).
- Recuperar un objeto de la pila (pop).
- Obtener el objeto de la cima (top) de la pila, sin extraerlo.
- Averiguar si hay algún objeto almacenado en la pila.
- Devolver el número de objetos almacenados en la pila.
Diseña la clase, decidiendo el nombre de cada método, sus parámetros y su valor de retorno. (Pista: Crea una clase Node, que sea la encargada de almacenar los valores de la pila)
 
              La solución se puede ver en el siguiente listado:
/*
 * LinkedStack.java
 *
 */
public class LinkedStack {
  class Node {
    Object elem;
    Node Next;
    public Node(Object o) {
      elem = o;
      Next = null;
    }
  }
  Node end;
  int size;
  public LinkedStack() {
    end = null;
    size = 0;
  }
  public void push(Object o) {
    Node new_node = new Node(o);
    if (end == null)
      end = new_node;
    else {
      new_node.Next = end;
      end = new_node;
    }
    size++;
  }; // inserts an object onto the stack
  public Object pop() {
    if (end == null)
      return null;
    ;
    Object o = end.elem;
    end = end.Next;
    size--;
    return o;
  }// Gets the object from the stack
  public boolean isEmpty() {
    return (size == 0);
  }
  public int size() {
    return size;
  }
  public Object end() {
    if (end == null)
      return null;
    else
      return end.elem;
  }
} // class LinkedStack
	  
              Haz una clase de prueba que lea líneas de un fichero y las imprima en orden inverso en pantalla. Por ejemplo, si el fichero tiene cuatro lineas "A", "B", "C" y "D", debe imprimir "D", "C", "B" y "A". La solución debe utilizar internamente una pila.
La solución se puede ver en el siguiente listado:
import java.io.*;
public class TestLinkedStack {
  public static void main(String args[]) {
    FileReader f = null; // to read files
    BufferedReader reader = null; // reading buffer
    String line = null; // read lines
    LinkedStack stack = new LinkedStack(); // initialization
    if (args.length < 1) {
      System.err.println("Please invoke the program like this:");
      System.err.println("\tLinkedStack file");
    }
    // opens the file
    try {
      f = new FileReader(args[0]);
      reader = new BufferedReader(f);
      while ((line = reader.readLine()) != null)
        stack.push(line);
    } catch (Exception e) {
      System.err.println("File not found " + args[0]);
      return;
    }
    // Gets the strings from the stack and prints them
    while ((line = (String) stack.pop()) != null) {
      System.out.println(line);
    }
  }
}
	  
             1.2. 
		Implementación de una cola con listas enlazadas
1.2. 
		Implementación de una cola con listas enlazadas
		
Mediante la resolución de este ejercicio se pretende que el alumno aprenda a implementar una cola utilizando listas enlazadas como estructura de datos.
Implementa una cola utilizando una lista enlazada como estructura de datos. La cola debe permitir almacenar cualquier tipo de objeto, e implementar al menos métodos para:
- Insertar un objeto en la cola (enqueue).
- Recuperar un objeto de la cola (dequeue).
- Obtener el primer objeto (first) de la cola, sin extraerlo.
- Averiguar si hay algún objeto almacenado en la cola (isEmpty).
- Devolver el número de objetos almacenados en la cola (size).
Diseña la clase, decidiendo el nombre de cada método, sus parámetros y su valor de retorno.
La solución se puede ver en el siguiente listado:
	      /*
 * LinkedQueue.java
 *
 */
public class LinkedQueue {
  class Node {
    Object elem;
    Node Next;
    public Node(Object o) {
      elem = o;
      Next = null;
    }
  }
  Node first;
  Node end;
  int size;
  public LinkedQueue() {
    end = null;
    size = 0;
  }
  public void enqueue(Object o) {
    Node new_node = new Node(o);
    if (first == null) {
      first = new_node;
      end = new_node;
    } else {
      end.Next = new_node;
      end = new_node;
    }
    size++;
  }; // inserts an object onto the queue
  public Object dequeue() {
    if (first == null)
      return null;
    ;
    Object o = first.elem;
    first = first.Next;
    size--;
    return o;
  } // gets the object from the queue
  public boolean isEmpty() {
    return (size == 0);
  }
  public int size() {
    return size;
  }
  public Object first() {
    if (first == null)
      return null;
    else
      return first.elem;
  }
} // class LinkedQueue
	  
              De igual modo, haz una clase de prueba que lea líneas de dos ficheros y las imprima en orden en pantalla. Por ejemplo, si el primer fichero tiene dos lineas "A" y "B" y el segundo fichero contiene dos líneas "C" y "D", debe imprimir "A", "B", "C" y "D". La solución debe utilizar internamente una cola.
La solución se puede ver en el siguiente listado:
	      import java.io.*;
public class TestLinkedQueue {
  public static void main(String args[]) {
    FileReader f = null; // to read files
    BufferedReader reader = null; // reading buffer
    String line = null; // read lines
    LinkedQueue queue = new LinkedQueue(); // initialization
    if (args.length < 1) {
      System.err.println("Please invoke the program like this:");
      System.err.println("\tLinkedQueue file1 file2");
    }
    // opens the first file
    try {
      f = new FileReader(args[0]);
      reader = new BufferedReader(f);
      while ((line = reader.readLine()) != null)
        queue.enqueue(line);
    } catch (Exception e) {
      System.err.println("File not found " + args[0]);
      return;
    }
    // opens the second file
    try {
      f = new FileReader(args[1]);
      reader = new BufferedReader(f);
      while ((line = reader.readLine()) != null)
        queue.enqueue(line);
    } catch (Exception e) {
      System.err.println("File not found " + args[1]);
      return;
    }
    // Gets the strings from the queue and prints them
    while ((line = (String) queue.dequeue()) != null) {
      System.out.println(line);
    }
  }
}
	  
            .png) 2. 
	
	Actividades para casa
2. 
	
	Actividades para casa
       2.1. 
		Trabajo con Listas doblemente enlazadas
2.1. 
		Trabajo con Listas doblemente enlazadas
		
Profundizar en el manejo de estructuras de datos listas.
La clase Lista representa una colección lineal de datos organizados en forma de lista enlazada. Esta lista presenta la peculiaridad de que almacena una referencia a un nodo concreto de la lista, al que apunta la referencia posicion, sobre el cual se realizan las operaciones de inserción y extracción de datos.
La referencia posicion debe poder avanzar y retroceder por la lista posición a posición. Por ello, resulta adecuado que la lista está doblemente enlazada, esto es, que cada nodo guarde una referencia no sólo al nodo que lo sigue en la lista, sino también al nodo que lo precede. La siguiente figura ilustra la estructura de la lista con un ejemplo:
Las operaciones básicas que debe permitir realizar esta lista son las siguientes:
Avanzar la referencia posicion: la referencia se mueve hacia delante tantas posiciones como se indique. Si la lista está vacía o el número de posiciones a avanzar no es mayor que cero, no se hace nada. Si se pide avanzar más posiciones de las que quedan en la lista, posicion avanzará sólo hasta el último nodo. Si posicion apunta a null antes de avanzar, se coloca en el primer nodo de la lista, y a continuación se avanza el número de posiciones indicado, descontando una (por ejemplo, si se pide avanzar 5 posiciones y posicion apunta a null inicialmente, primero se coloca apuntando al primer nodo, y después se avanzan 4 posiciones desde este).
Retroceder la referencia posicion: la referencia posicion debe retroceder tantas posiciones como se indique. Si la lista está vacía o el número de posiciones a retroceder no es mayor que cero, no se hace nada. Si se pide retroceder más posiciones que el primer elemento de la lista, la referencia se quedará en este primer elemento. 
Insertar un nuevo dato en la lista: se inserta un nuevo nodo para guardar este dato, inmediatamente a continuación de posicion, esto es, el nodo nuevo seguirá al actualmente referenciado por posicion. Cuando posicion es null, el nuevo nodo se inserta en la primera posición de la lista. Al finalizar la inserción, la referencia posicion debe apuntar al nuevo nodo introducido.
Extraer un dato de la lista: devuelve el dato del nodo apuntado por posicion, borra dicho nodo, y avanza posicion al siguiente nodo (queda apuntando a null si no hay siguiente nodo). Si posicion apunta a null, no se extrae nada y se devuelve null.
Programa la clase Nodo, la clase Lista y los siguientes métodos de ésta:
	public void avanzar(int posiciones) {...}
  
         public void retroceder(int posiciones) {...}
  
	public void insertar(Object dato) {...} 
  
	public Object extraer() {...}
  
La solución se puede ver en el siguiente listado:
/**
 * <p>Universidad Carlos III de Madrid</p>
 *
 * @author Departamento de Ingenieria Telematica. UC3M.
 * @version 1.0
 */
/**
 * 
 * <p>
 * 'Playing' with double linked list
 * </p>
 * 
 * 
 * @author Departamento de Ingenieria Telematica. UC3M.
 * @version 1.0
 */
public class List {
  // nested class
  public class Node {
    Node next;
    Node previous;
    Object data;
    public Node(Node next, Object data) {
      this.next = next;
      this.data = data;
    }
    public Node(Object elem) {
      this(null, elem);
    }
  }
  private Node first;
  private Node currentPosition;
  /**
   * Increments current position -numPositions-
   */
  public void forward(int numPositions) {
    if (numPositions > 0 && first != null) {
      int positionsForward = numPositions;
      if (currentPosition == null) {
        currentPosition = first;
        positionsForward--;
      }
      while (currentPosition.next != null && positionsForward > 0) {
        currentPosition = currentPosition.next;
        positionsForward--;
      }
    }
  }
  /**
   * Inserts an object in --currentPosition-.
   */
  public void insert(Object data) {
    Node node = new Node(data);
    if (currentPosition == null) {
      // inserts in first node
      node.next = first;
      if (first != null) {
        first.previous = node;
      }
      first = node;
    } else {
      // the node isn't the first.
      node.next = currentPosition.next;
      node.previous = currentPosition;
      if (currentPosition.next != null) {
        currentPosition.next.previous = node;
      }
      currentPosition.next = node;
    }
    // updates current position
    currentPosition = node;
  }
  /**
   * Delete the node referenced by -currentPosition-.
   * 
   * @return The object to delete
   */
  public Object extract() {
    Object data = null;
    if (currentPosition != null) {
      data = currentPosition.data;
      // 'delete' the node
      if (first == currentPosition) {
        first = currentPosition.next;
      } else {
        currentPosition.previous.next = currentPosition.next;
      }
      if (currentPosition.next != null) {
        currentPosition.next.previous = currentPosition.previous;
      }
      // next position
      currentPosition = currentPosition.next;
    }
    return data;
  }
  /** testing program */
  public String toString() {
    Node aux = first;
    StringBuffer sb = new StringBuffer();
    while (aux != null) {
      sb.append(aux.data);
      aux = aux.next;
    }
    return sb.toString();
  }
  /**
   * Go back -numPositions-
   */
  public void back(int numPositions) {
    if (numPositions <= 0 || first == null || currentPosition == null)
      return;
    int positionsBack = numPositions;
    while (currentPosition != null && positionsBack > 0) {
      currentPosition = currentPosition.previous;
      positionsBack--;
    }
  }
  /** Test method */
  public static void main(String[] args) {
    List list = new List();
    list.insert("Node1");
    list.insert("Node2");
    System.out.println("Initial list: " + list.toString());
    System.out
        .println("Data in current position: " + list.currentPosition.data);
    System.out.println("Add Node: ");
    list.insert("Node3");
    System.out
        .println("Data in current position: " + list.currentPosition.data);
    System.out.println("List: " + list.toString());
    list.back(1);
    System.out.println("Go back one position");
    System.out
        .println("Data in current position: " + list.currentPosition.data);
    System.out.println("List: " + list.toString());
    list.insert("Node4");
    System.out
        .println("Data in current position: " + list.currentPosition.data);
    System.out.println("List: " + list.toString());
    list.extract();
    System.out.println("delete... ");
    System.out
        .println("Data in current position: " + list.currentPosition.data);
    System.out.println("List: " + list.toString());
    System.out.println("Go forward 7 ");
    list.forward(7);
    System.out
        .println("Data in current position: " + list.currentPosition.data);
    System.out.println("List: " + list.toString());
    list.back(1);
    list.extract();
    System.out
        .println("Data in current position: " + list.currentPosition.data);
    System.out.println("List: " + list.toString());
    list.extract();
    System.out.println("Current position: " + list.currentPosition);
    System.out.println("List: " + list.toString());
    list.forward(1);
    list.extract();
    System.out.println("Current position: " + list.currentPosition);
    System.out.println("List: " + list.toString());
    list.extract();
    System.out.println("Current position: " + list.currentPosition);
    System.out.println("List: " + list.toString());
  }
}
	  
               2.2. Implementación de una Deque (Bicola) mediante lista
  doblemente enlazada
2.2. Implementación de una Deque (Bicola) mediante lista
  doblemente enlazada Aprender a trabajar con Deques (Bicolas).
Una Deque, también conocida como
    Bicola, es una estructura lineal de datos que permite
    insertar y eliminar elementos por ambos extremos. Para su construcción,
    nos basaremos en el uso de una Lista Doblemente Enlazada
    (LDE) en la que implementaremos los métodos correspondientes
    para lograr dicho comportamiento.
El comportamiento de una Deque se puede
    lograr mediante la implementación de la siguiente interfaz que muestra sus
    métodos principales:
/**
 * Interface for a Deque.
 * 
 * @author DIT-UC3M
 * 
 */
public interface Dqueue {
  void insertFirst(Object obj);
  void insertLast(Object obj);
  Object removeFirst();
  Object removeLast();
  int size();
}
              Para implemetar una Deque mediante una LDE
    realiza las siguientes tareas:
Programa la clase DNode que constituye el
        nodo de una Lista Doblemente Enlazada (LDE). Dicho nodo contendrá las
        referencias prev y next a los nodos anterior
        y posterior de la misma, así como una referencia de tipo
        Object al dato que se almacena.
Declara la clase DLDqueue que
        implementará una Deque basada en una LDE. Para ello,
        deberá implementar el interfaz Dqueue mencionado
        anteriormente.
Añade los atributos head y
        tail que representan los nodos extremos de la LDE y el
        atributo entero size que permite almacenar el tamaño de
        la misma.
Programa el constructor de la clase
        DLDqueue que inicializa las referencias head
        y tail a un nodo de la LDE que esté vacío y establece el
        tamaño a 0.
Programa los siguientes métodos del interfaz
        Deque:
                            public void insertFirst(Object obj)
                          
Este método inserta un objeto al comienzo de la LDE.
                            public void insertLast(Object obj)
                          
Este método inserta un objeto al final de la LDE.
                            public Object removeFirst()
                          
Este método extrae un objeto del comienzo de la
            LDE y lo elimina de la lista. Si no existe dicho objeto, devuelve
            null.
                            public Object removeLast()
                          
Este método extrae un objeto del final de la LDE y
            lo elimina de la lista.Si no existe dicho objeto, devuelve
            null.
                            public int size()
                          
Este método devuelve el tamaño de la lista LDE.
Por úlitmo, implementa el método public
            String toString() que permite imprimir el contenido de la
            Deque por pantalla con el siguiente formato (los
            valores que aparecen son de ejemplo):
                      head [ 1-2-3-4-5 ] tail
                    
¡¡ NOTA IMPORTANTE !!
En este ejercicio, hemos practicado el diseño y la
    implementación de una Deque. Sin embargo, en el API de Java
    existen clases que también implementan este tipo de estructuras de datos.
    Una Deque está definida por la siguiente interfaz: Interface
    Deque. Existen dos posibles implementaciones de la misma, una
    basada en arrays (ArrayDeque)
    y otra basada en Listas Enlazadas (LinkedList).
Las soluciones se pueden ver en los siguientes listados:
/**
 * An Implementation of a Double Linked List Node
 * 
 * @author DIT-UC3M
 * 
 */
public class DNode {
  DNode next, prev;
  Object val;
  public DNode() {
    this.next = null;
    this.prev = null;
    this.val = null;
  }
  public DNode(Object val, DNode first, DNode last) {
    this.next = first;
    this.prev = last;
    this.val = val;
  }
  public DNode getNext() {
    return next;
  }
  public void setNext(DNode next) {
    this.next = next;
  }
  public DNode getPrev() {
    return prev;
  }
  public void setPrev(DNode prev) {
    this.prev = prev;
  }
  public Object getVal() {
    return val;
  }
  public void setVal(Object val) {
    this.val = val;
  }
}
	  
                
                /**
 * Class that implements a Deque with a Double Linked List.
 * 
 * @author DIT-UC3M
 * 
 */
public class DLDqueue implements Dqueue {
  DNode head, tail;
  int size;
  public DLDqueue() {
    head = new DNode();
    tail = new DNode();
    head.setNext(tail);
    tail.setPrev(head);
    size = 0;
  }
  public void insertFirst(Object obj) {
    DNode h = head;
    DNode node = new DNode();
    node.setVal(obj);
    node.setNext(h);
    h.setPrev(node);
    head = node;
    if (size == 0)
      tail = node;
    size++;
  }
  public void insertLast(Object obj) {
    DNode t = tail;
    DNode node = new DNode();
    node.setVal(obj);
    node.setPrev(t);
    t.setNext(node);
    tail = node;
    if (size == 0)
      head = node;
    size++;
  }
  public Object removeFirst() {
    if (head == null)
      return null;
    Object val = head.getVal();
    head = head.getNext();
    size--;
    return val;
  }
  public Object removeLast() {
    if (tail == null)
      return null;
    Object val = tail.getVal();
    tail = tail.getPrev();
    size--;
    return val;
  }
  public int size() {
    return size;
  }
  public String toString() {
    String s = "head [";
    DNode aux = head;
    for (int i = 0; i < size; i++) {
      s += aux.getVal();
      if (aux == tail) {
        break;
      }
      s += "-";
      aux = aux.getNext();
    }
    return s + "] tail";
  }
}
	  
               2.3. Implementación de pilas y colas con
  Deques (Bicolas)
2.3. Implementación de pilas y colas con
  Deques (Bicolas) Aprender cómo hacer que una Deque se comporte
    como una Pila o una Cola.
En este ejercicio, vamos a aprovechar la flexibilidad de
    una Deque para hacer que se comporte como si fueran una
    Pila o una Cola. Para hacerlo, obligaremos a que
    la clase que la use implemente el interfaz correspondiente que se muestra
    a continuación:
Interfaz de una Pila:
/**
 * Interface for a Stack.
 * 
 * @author DIT-UC3M
 * 
 */
public interface Stack {
  void push(Object obj);
  Object pop();
  int size();
}
                  Interfaz de una Cola:
/**
 * Interface for a Queue.
 * 
 * @author DIT-UC3M
 * 
 */
public interface Queue {
  public void enqueue(Object obj);
  public Object dequeue();
  int size();
}
                  A continuación, vamos a usar una Deque para
    implementar tanto una Pila como una Cola de
    datos. Para ello, realiza las siguientes tareas:
Para implemetar una Pila mediante una
        Dqueue:
Declara la clase DQStack que se
            comportará como una Pila. Para ello deberá
            implementar el interfaz Stack mencionado
            anteriormente.
Añade un atributo data que sea de
            tipo DLDqueue y que constituye la lista donde se
            almacenarán los datos de la pila.
Programa el constructor de la clase
            DQStack que inicializa el atributo data
            creando un objeto de la clase DLDqueue.
Programa los siguientes métodos del interfaz
            Stack:
                                  public void push(Object obj)
                                
Este método inserta un objeto al comienzo de
                la DLDqueue.
                                  public Object pop()
                                
Este método extrae un objeto del comienzo de
                la DLDqueue y lo elimina de la lista. Si no
                existe dicho objeto, devuelve null.
                                  public int size()
                                
Este método devuelve el tamaño de la
                DLDqueue.
Implementa el método main para probar
            la Pila. Deberá crear una instancia de una
            DQStack, apilar y desapilar una serie de datos
            imprimiendo por pantalla el contenido y el tamaño de la
            pila.
Para implemetar una Cola mediante una
        DLDqueue se pide:
Declara la clase DQQueue que debe
            comportarse como una Cola. Para ello deberá
            implementar el interfaz Queue proporcionado.
Añade un atributo data que sea de
            tipo DLDqueue y que constituye la lista donde se
            almacenarán los datos de la cola.
Programa el constructor de la clase
            DQQueue que inicializa el atributo data
            creando un objeto de la clase DLDqueue.
Programa los siguientes métodos del interfaz
            Queue:
                                  public void enqueue(Object obj)
                                
Este método inserta un objeto al comienzo de
                la DLDqueue.
                                  public Object dequeue()
                                
Este método extrae un objeto del final de la
                DLDqueue y lo elimina de la lista.Si no existe
                dicho objeto, devuelve null.
                                  public int size()
                                
Este método devuelve el tamaño de la
                DLDqueue.
Por último, implementa el método main
            para probar la Cola. Dicho método deberá crear una
            instancia de una DQQueue, encolar y desencolar una
            serie de datos imprimiendo por pantalla el contenido y el tamaño
            de la lista.
Las soluciones se pueden ver en los siguientes listados:
/**
 * Interface for a Stack.
 * 
 * @author DIT-UC3M
 * 
 */
public interface Stack {
  void push(Object obj);
  Object pop();
  int size();
}
	  
                
                /**
 * Interface for a Queue.
 * 
 * @author DIT-UC3M
 * 
 */
public interface Queue {
  public void enqueue(Object obj);
  public Object dequeue();
  int size();
}
	  
                
                /*
 * Class that implements a Stack with a Deque
 *  
 * @author DIT-UC3M 
 *
 */
public class DQStack implements Stack {
  private Dqueue data;
  public DQStack() {
    data = new DLDqueue();
  }
  public void push(Object obj) {
    data.insertFirst(obj);
  }
  public Object pop() {
    return data.removeFirst();
  }
  public int size() {
    return data.size();
  }
  public String toString() {
    return data.toString();
  }
  public static void main(String[] args) {
    DQStack stack = new DQStack();
    for (int i = 0; i < 5; i++) {
      stack.push(i);
    }
    System.out.println("Printing stack: " + stack);
    int s = stack.size();
    System.out.println("Printing size: " + stack.size());
    Object o = stack.pop();
    System.out.println("Pop element = " + o);
    System.out.println("Printing stack: " + stack);
  }
}
	  
                
                /*
 * Class that implements a Queue with a Deque
 *  
 * @author DIT-UC3M 
 *
 */
public class DQQueue implements Queue {
  private Dqueue data;
  public DQQueue() {
    data = new DLDqueue();
  }
  public void enqueue(Object obj) {
    data.insertFirst(obj);
  }
  public Object dequeue() {
    return data.removeLast();
  }
  public int size() {
    return data.size();
  }
  public String toString() {
    return data.toString();
  }
  public static void main(String[] args) {
    DQQueue queue = new DQQueue();
    for (int i = 0; i < 5; i++) {
      queue.enqueue(i);
    }
    System.out.println("Printing queue: " + queue);
    int s = queue.size();
    System.out.println("Printing size: " + queue.size());
    Object o = queue.dequeue();
    System.out.println("Deque element = " + o);
    System.out.println("Printing queue: " + queue);
  }
}