Nas teóricas falamos de mais tipos de listas ligadas, incluindo a noção de listas circulares, onde o último elemento "aponta" para o primeiro, de forma circular:
// ----------------------------------------------------------- // Estruturas de Dados 2023/2024 (CC1007) - DCC/FCUP // https://www.dcc.fc.up.pt/~miguel-areias/teaching/2324/ed/ // ----------------------------------------------------------- // Classe com um no generico (Pedro Ribeiro @ DCC-FCUP) // ----------------------------------------------------------- public class Node<T> { private T value; // Valor guardado no no private Node<T> next; // Referencia para o proximo no da lista // Construtor Node(T v, Node<T> n) { value = v; next = n; } // Getters e Setters public T getValue() { return value; } public Node<T> getNext() { return next; } public void setValue(T v) { value=v; } public void setNext(Node<T> n) { next = n; } }
// ----------------------------------------------------------- // Estruturas de Dados 2023/2024 (CC1007) - DCC/FCUP // https://www.dcc.fc.up.pt/~miguel-areias/teaching/2324/ed/ // ----------------------------------------------------------- // Lista ligada circular simples // (implementada apenas com "last": o primeiro e o last.next) // (Pedro Ribeiro @ DCC-FCUP) // ----------------------------------------------------------- public class CircularLinkedList<T> { private Node<T> last; // Ultimo no da lista private int size; // Tamanho da lista // Construtor (cria lista vazia) CircularLinkedList() { last = null; size = 0; } // Retorna o tamanho da lista public int size() { return size; } // Devolve true se a lista estiver vazia ou falso caso contrario public boolean isEmpty() { return (size == 0); } // Retorna o primeiro valor da lista (ou null se a lista for vazia) public T getFirst() { if (isEmpty()) return null; return last.getNext().getValue(); } // Retorna o ultimo valor da lista (ou null se a lista for vazia) public T getLast() { if (isEmpty()) return null; return last.getValue(); } // Roda a lista (o primeiro passa a ser o novo ultimo da lista) public void rotate() { if (!isEmpty()) // Se estiver vazia nao faz nada last = last.getNext(); } // Adiciona v ao inicio da lista public void addFirst(T v) { if (isEmpty()) { last = new Node<T>(v, null); last.setNext(last); // Apontar para si proprio em "loop" } else { Node<T> newNode = new Node<T>(v, last.getNext()); last.setNext(newNode); } size++; } // Adiciona v ao final da lista public void addLast(T v) { addFirst(v); last = last.getNext(); } // Remove o primeiro elemento da lista (se for vazia nao faz nada) public void removeFirst() { if (isEmpty()) return; if (size == 1) last = null; else last.setNext(last.getNext().getNext()); size--; } // Remove o ultimo elemento da lista (se for vazia nao faz nada) public void removeLast() { if (isEmpty()) return; if (size == 1) { last = null; } else { Node<T> cur = last.getNext(); for (int i=0; i<size-2; i++) cur = cur.getNext(); last = cur; last.setNext(last.getNext().getNext()); } size--; } // Converte a lista para uma String public String toString() { String str = "{"; if (!isEmpty()) { Node<T> cur = last.getNext(); for (int i=0; i<size; i++) { str += cur.getValue(); if (cur != last) str += ","; cur = cur.getNext(); } } str += "}"; return str; } }
// ----------------------------------------------------------- // Estruturas de Dados 2023/2024 (CC1007) - DCC/FCUP // https://www.dcc.fc.up.pt/~miguel-areias/teaching/2324/ed/ // ----------------------------------------------------------- // Exemplo de utilizacao da lista circular // (Pedro Ribeiro @ DCC-FCUP) // ----------------------------------------------------------- public class TestCircularLinkedList { public static void main(String[] args) { // Simulando processos (A,B,C,D) e escalonamento round-robin // Criacao de lista de caracteres CircularLinkedList<Character> list = new CircularLinkedList<Character>(); // Adicionando letras de 'A' a 'E' list.addLast('A'); list.addLast('B'); list.addLast('C'); list.addLast('D'); // Mostrando rotacao a funcionar System.out.println("8 rondas round-robin"); for (int i=1; i<=8; i++) { System.out.println(i + ": " + list); list.rotate(); } // Inserir e retirar elemento no final list.addLast('E'); System.out.println("addLast('E'): " + list); list.removeLast(); System.out.println("removeLast(): " + list); // Inserir retirar elemento no inicio list.addFirst('F'); System.out.println("addFirst('F'): " + list); list.removeFirst(); System.out.println("removeFirst(): " + list); } }
Compile o código e execute a classe TestCircularLinkedList para ver as listas circulares em funcionamento. Procure acompanhar cada uma das linhas de código e o seu efeito. Note em particular o que faz o método rotate() que "roda" os elementos, colocando o primeiro elemento no final da lista e avançado o segundo elemento para o início da fila:
// ----------------------------------------------------------- // Estruturas de Dados 2023/2024 (CC1007) - DCC/FCUP // https://www.dcc.fc.up.pt/~miguel-areias/teaching/2324/ed/ // ----------------------------------------------------------- // Classe com um no generico com dupla ligacao // (Pedro Ribeiro @ DCC-FCUP) // ----------------------------------------------------------- public class DNode<T> { private T value; // Valor guardado no no private DNode<T> prev; // Referencia para o no anterior da lista private DNode<T> next; // Referencia para o proximo no da lista // Construtor DNode(T v, DNode<T> p, DNode<T> n) { value = v; prev = p; next = n; } // Getters e Setters public T getValue() { return value; } public DNode<T> getPrev() { return prev; } public DNode<T> getNext() { return next; } public void setValue(T v) { value=v; } public void setPrev(DNode<T> p) { prev = p; } public void setNext(DNode<T> n) { next = n; } }
// ----------------------------------------------------------- // Estruturas de Dados 2023/2024 (CC1007) - DCC/FCUP // https://www.dcc.fc.up.pt/~miguel-areias/teaching/2324/ed/ // ----------------------------------------------------------- // Lista duplamente ligada (Pedro Ribeiro @ DCC-FCUP) // (implementada com sentinelas - nos "dummy" no inicio e no fim // que facilitam a implementacao e evitam casos excepcionais) // ----------------------------------------------------------- public class DoublyLinkedList<T> { private DNode<T> first; // Primeiro no da lista private DNode<T> last; // Ultimo no da lista private int size; // Tamanho da lista // Construtor (cria lista vazia com dois nos sentinelas) DoublyLinkedList() { first = new DNode<T>(null, null, null); last = new DNode<T>(null, first, null); // Antes do ultimo vem o primeiro first.setNext(last); // A seguir ao primeiro vem o ultimo size = 0; } // Retorna o tamanho da lista public int size() { return size; } // Devolve true se a lista estiver vazia ou falso caso contrario public boolean isEmpty() { return (size == 0); } // Retorna o primeiro valor da lista (ou null se a lista for vazia) public T getFirst() { if (isEmpty()) return null; return first.getNext().getValue(); } // Retorna o ultimo valor da lista (ou null se a lista for vazia) public T getLast() { if (isEmpty()) return null; return last.getPrev().getValue(); } // Adiciona v ao inicio da lista public void addFirst(T v) { addBetween(v, first, first.getNext()); } // Adiciona v ao final da lista public void addLast(T v) { addBetween(v, last.getPrev(), last); } // Adiciona elemento entre dois nos n1 e n2 private void addBetween(T v, DNode<T> n1, DNode<T> n2) { DNode<T> newNode = new DNode<T>(v, n1, n2); n1.setNext(newNode); n2.setPrev(newNode); size++; } // Remove o primeiro elemento da lista (se for vazia nao faz nada) public void removeFirst() { if (isEmpty()) return; remove(first.getNext()); } // Remove o ultimo elemento da lista (se for vazia nao faz nada) public void removeLast() { if (isEmpty()) return; remove(last.getPrev()); } // Remove um no da lista private void remove(DNode<T> n) { DNode<T> prev = n.getPrev(); DNode<T> next = n.getNext(); prev.setNext(next); next.setPrev(prev); size--; } // Converte a lista para uma String public String toString() { String str = "{"; DNode<T> cur = first.getNext(); for (int i=0; i<size; i++) { str += cur.getValue(); cur = cur.getNext(); if (cur != last) str += ","; } str += "}"; return str; } }
// ----------------------------------------------------------- // Estruturas de Dados 2023/2024 (CC1007) - DCC/FCUP // https://www.dcc.fc.up.pt/~miguel-areias/teaching/2324/ed/ // ----------------------------------------------------------- // Exemplo de utilizacao da lista duplamente ligada // Pedro Ribeiro @ DCC-FCUP // ----------------------------------------------------------- public class TestDoublyLinkedList { public static void main(String[] args) { // Criacao de lista de inteiros DoublyLinkedList<Integer> list = new DoublyLinkedList<Integer>(); // Escrevendo lista (no inicio esta vazia) System.out.println(list); // Verificando se esta vazia System.out.println("isEmpty? " + list.isEmpty()); // Adicionando numeros de 1 a 5 ao final da lista for (int i=1; i<=5; i++) list.addLast(i); System.out.println(list); // Adicionando numeros de 6 a 10 ao inicio da lista for (int i=6; i<=10; i++) list.addFirst(i); System.out.println(list); // Verificando o tamanho da lista System.out.println("size = " + list.size()); // Retirando o primeiro elemento list.removeFirst(); System.out.println(list); // Retirando o ultimo elemento list.removeLast(); System.out.println(list); // Verificando se esta vazia System.out.println("isEmpty? " + list.isEmpty()); // Escreve o primeiro e ultimo elementos System.out.println("getFirst() = " + list.getFirst()); System.out.println("getLast() = " + list.getLast()); } }
Compile o código e execute a classe TestDoublyLinkedList para ver as listas duplamente ligadas em funcionamento. Procure acompanhar cada uma das linhas de código e o seu efeito.
Um TAD Pilha é uma sequência de elementos LIFO (Last-In-First-Out) onde temos duas operações para modificar o conteúdo:
// ----------------------------------------------------------- // Estruturas de Dados 2023/2024 (CC1007) - DCC/FCUP // https://www.dcc.fc.up.pt/~miguel-areias/teaching/2324/ed/ // ----------------------------------------------------------- // Interface para o TAD Pilha // (Pedro Ribeiro @ DCC-FCUP) // ----------------------------------------------------------- public interface MyStack<T> { // Metodos que modificam a pilha public void push(T v); // Coloca um valor no topo da pilha public T pop(); // Retira e retorna o valor no topo da pilha // Metodos que acedem a informacao (sem modificar) public T top(); // Retorna valor no topo da pilha public int size(); // Retorna quantidade de elementos na pilha public boolean isEmpty(); // Indica se a pilha esta vazia }
// ----------------------------------------------------------- // Estruturas de Dados 2023/2024 (CC1007) - DCC/FCUP // https://www.dcc.fc.up.pt/~miguel-areias/teaching/2324/ed/ // ----------------------------------------------------------- // Exemplo de utilizacao do TAD Pilha // (Pedro Ribeiro @ DCC-FCUP) // ----------------------------------------------------------- public class TestMyStack { public static void main(String[] args) { // Criacao da pilha MyStack<Integer> s = new LinkedListStack<Integer>(); //MyStack<Integer> s = new ArrayStack<Integer>(); // Exemplo de insercao de elementos na pilha for (int i=1; i<=8; i++) s.push(i); // insere i no topo da pilha System.out.println(s); // Exemplo de remocao de elementos na pilha for (int i=0; i<4; i++) { int aux = s.pop(); // retira o elemento no topo da pilha System.out.println("s.pop() = " + aux); } System.out.println(s); // Exemplo de utilizacao dos outros metodos System.out.println("s.size() = " + s.size()); System.out.println("s.isEmpty() = " + s.isEmpty()); System.out.println("s.top() = " + s.top()); } }
// ----------------------------------------------------------- // Estruturas de Dados 2023/2024 (CC1007) - DCC/FCUP // https://www.dcc.fc.up.pt/~miguel-areias/teaching/2324/ed/ // ----------------------------------------------------------- // Implementacao do TAD Pilha usando lista duplamente ligada // (Pedro Ribeiro @ DCC-FCUP) // ----------------------------------------------------------- public class LinkedListStack<T> implements MyStack<T> { private DoublyLinkedList<T> list; LinkedListStack() { list = new DoublyLinkedList<T>();} public void push(T v) { list.addFirst(v); } public T pop() { T ans = list.getFirst(); list.removeFirst(); return ans; } public T top() { return list.getFirst();} public int size() {return list.size();} public boolean isEmpty() {return list.isEmpty();} public String toString() {return list.toString();} }
// ----------------------------------------------------------- // Estruturas de Dados 2023/2024 (CC1007) - DCC/FCUP // https://www.dcc.fc.up.pt/~miguel-areias/teaching/2324/ed/ // ----------------------------------------------------------- // Implementacao de Pilha usando array // (Pedro Ribeiro @ DCC-FCUP) // // Nota: o Java proibe a criacao de arrays genericos. Para efeitos // de demonstracao, usamos aqui um array de Objects e um cast // (que prova um aviso de compilacao que e "evitado" com a linha do // Supress Warning). De um modo geral, para genericos, o ideal seria // nao usar um array "nativo", mas sim algo como um ArrayList, mas // aqui queremos demonstrar como pode ser feito. // ----------------------------------------------------------- import java.util.Arrays; public class ArrayStack<T> implements MyStack<T> { public static final int CAPACITY = 1000; // Capacidade padrao private T[] data; // Array para conter elementos private int size; // Quantidade de elementos ArrayStack() { this(CAPACITY); } @SuppressWarnings("unchecked") ArrayStack(int capacity) { data = (T[]) new Object[capacity]; size = 0; } public void push(T v) { if (size >= data.length) throw new RuntimeException("Stack is full"); data[size++] = v; } public T pop() { if (isEmpty()) return null; return data[--size]; } public T top() { return data[size-1];} public int size() {return size;} public boolean isEmpty() {return (size==0);} public String toString() { String str = "["; for (int i=size-1; i>=0; i--) { str += data[i]; if (i>0) str += ","; } str += "]"; return str; } }
Compile o código e execute a classe TestMyStack para ver a pilha em funcionamento.
Procure acompanhar cada uma das linhas de código e o seu efeito. Note em particular os métodos do TAD (basta ver o interface) e como criar a Pilha (a variável do tipo do interface atribuída a uma implementação desse mesmo interface).
Nota: se quiser testar o método pode optar por adicionar um main à sua classe ED194 (não será chamado no Mooshak) ou criar uma classe adicional no seu computador para fazer chamadas a ED194.
Um TAD Fila é uma sequência de elementos FIFO (First-In-First-Out) onde temos duas operações para modificar o conteúdo:
// ----------------------------------------------------------- // Estruturas de Dados 2023/2024 (CC1007) - DCC/FCUP // https://www.dcc.fc.up.pt/~miguel-areias/teaching/2324/ed/ // ----------------------------------------------------------- // Interface para o TAD Fila // (Pedro Ribeiro @ DCC-FCUP) // ----------------------------------------------------------- public interface MyQueue<T> { // Metodos que modificam a fila public void enqueue(T v); // Coloca um valor no final da fila public T dequeue(); // Retira e retorna o valor no inicio da fila // Metodos que acedem a informacao (sem modificar) public T first(); // Retorna valor no inicio da fila public int size(); // Retorna quantidade de elementos na fila public boolean isEmpty(); // Indica se a fila esta vazia }
// ----------------------------------------------------------- // Estruturas de Dados 2023/2024 (CC1007) - DCC/FCUP // https://www.dcc.fc.up.pt/~miguel-areias/teaching/2324/ed/ // ----------------------------------------------------------- // Exemplo de utilizacao do TAD Fila // (Pedro Ribeiro @ DCC-FCUP) // ----------------------------------------------------------- public class TestMyQueue { public static void main(String[] args) { // Criacao da fila MyQueue<Integer> q = new LinkedListQueue<Integer>(); // Exemplo de insercao de elementos na fila for (int i=1; i<=8; i++) q.enqueue(i); // insere i no final da fila System.out.println(q); // Exemplo de remocao de elementos da fila for (int i=0; i<4; i++) { int aux = q.dequeue(); // retira o elemento no inicio da fila System.out.println("q.dequeue() = " + aux); } System.out.println(q); // Exemplo de utilizacao dos outros metodos System.out.println("q.size() = " + q.size()); System.out.println("q.isEmpty() = " + q.isEmpty()); System.out.println("q.first() = " + q.first()); } }
// ----------------------------------------------------------- // Estruturas de Dados 2023/2024 (CC1007) - DCC/FCUP // https://www.dcc.fc.up.pt/~miguel-areias/teaching/2324/ed/ // ----------------------------------------------------------- // Implementacao do TAD Fila usando lista duplamente ligada // (Pedro Ribeiro @ DCC-FCUP) // ----------------------------------------------------------- public class LinkedListQueue<T> implements MyQueue<T> { private DoublyLinkedList<T> list; LinkedListQueue() { list = new DoublyLinkedList<T>();} public void enqueue(T v) { list.addLast(v); } public T dequeue() { T ans = list.getFirst(); list.removeFirst(); return ans; } public T first() { return list.getFirst();} public int size() {return list.size();} public boolean isEmpty() {return list.isEmpty();} public String toString() {return list.toString();} }
Compile o código e execute a classe TestMyQueue para ver a Fila em funcionamento.
Procure acompanhar cada uma das linhas de código e o seu efeito. Note em particular os métodos do TAD (basta ver o interface) e como criar a Fila (a variável do tipo do interface atribuída a uma implementação desse mesmo interface).