diff --git a/src/main/java/com/thealgorithms/datastructures/queues/Deques.java b/src/main/java/com/thealgorithms/datastructures/queues/Deque.java similarity index 61% rename from src/main/java/com/thealgorithms/datastructures/queues/Deques.java rename to src/main/java/com/thealgorithms/datastructures/queues/Deque.java index 5c4e9b64..4cfa2b44 100644 --- a/src/main/java/com/thealgorithms/datastructures/queues/Deques.java +++ b/src/main/java/com/thealgorithms/datastructures/queues/Deque.java @@ -1,5 +1,7 @@ package com.thealgorithms.datastructures.queues; +import java.util.NoSuchElementException; + /** * A [deque](https://en.wikipedia.org/wiki/Double-ended_queue) is short for a * double ended queue pronounced "deck" and sometimes referred to as a head-tail @@ -9,50 +11,24 @@ package com.thealgorithms.datastructures.queues; * * @author [Ian Cowan](https://github.com/iccowan) */ -public class Deques { +public class Deque { /** * Node for the deque */ - class DequeNode { - - /** - * Value of the node - */ + private static class DequeNode { S val; - - /** - * Next node in the deque from this node - */ DequeNode next = null; - - /** - * Previous node in the deque from this node - */ DequeNode prev = null; - /** - * Constructor - */ DequeNode(S val) { this.val = val; } } - /** - * Head of the deque - */ - DequeNode head = null; - - /** - * Tail of the deque - */ - DequeNode tail = null; - - /** - * Size of the deque - */ - int size = 0; + private DequeNode head = null; + private DequeNode tail = null; + private int size = 0; /** * Adds the specified value to the head of the deque @@ -60,16 +36,12 @@ public class Deques { * @param val Value to add to the deque */ public void addFirst(T val) { - // Create a new node with the given value - DequeNode newNode = new DequeNode(val); + DequeNode newNode = new DequeNode<>(val); - // Add the node - if (head == null) { - // If the deque is empty, add the node as the head and tail + if (isEmpty()) { head = newNode; tail = newNode; } else { - // If the deque is not empty, insert the node as the new head newNode.next = head; head.prev = newNode; head = newNode; @@ -84,20 +56,15 @@ public class Deques { * @param val Value to add to the deque */ public void addLast(T val) { - // Create a new node with the given value - DequeNode newNode = new DequeNode(val); - - // Add the node + DequeNode newNode = new DequeNode<>(val); if (tail == null) { - // If the deque is empty, add the node as the head and tail head = newNode; + tail = newNode; } else { - // If the deque is not empty, insert the node as the new tail newNode.prev = tail; tail.next = newNode; + tail = newNode; } - tail = newNode; - size++; } @@ -105,33 +72,21 @@ public class Deques { * Removes and returns the first (head) value in the deque * * @return the value of the head of the deque + * @throws NoSuchElementException if the deque is empty */ public T pollFirst() { - // If the head is null, return null if (head == null) { - return null; + throw new NoSuchElementException("Deque is empty"); } - // First, let's get the value of the old head T oldHeadVal = head.val; - - // Now, let's remove the head if (head == tail) { - // If there is only one node, remove it head = null; tail = null; } else { - // If there is more than one node, fix the references - head.next.prev = null; - DequeNode oldHead = head; head = head.next; - - // Can be considered unnecessary... - // Unlinking the old head to make sure there are no random - // references possibly affecting garbage collection - oldHead.next = null; + head.prev = null; } - size--; return oldHeadVal; } @@ -140,32 +95,21 @@ public class Deques { * Removes and returns the last (tail) value in the deque * * @return the value of the tail of the deque + * @throws NoSuchElementException if the deque is empty */ public T pollLast() { - // If the tail is null, return null if (tail == null) { - return null; + throw new NoSuchElementException("Deque is empty"); } - // Let's get the value of the old tail T oldTailVal = tail.val; - - // Now, remove the tail if (head == tail) { - // If there is only one node, remove it head = null; tail = null; } else { - // If there is more than one node, fix the references - tail.prev.next = null; - DequeNode oldTail = tail; tail = tail.prev; - - // Similarly to above, can be considered unnecessary - // See `pollFirst()` for explanation - oldTail.prev = null; + tail.next = null; } - size--; return oldTailVal; } @@ -173,19 +117,19 @@ public class Deques { /** * Returns the first (head) value of the deque WITHOUT removing * - * @return the value of the head of the deque + * @return the value of the head of the deque, or null if empty */ public T peekFirst() { - return head.val; + return head != null ? head.val : null; } /** * Returns the last (tail) value of the deque WITHOUT removing * - * @return the value of the tail of the deque + * @return the value of the tail of the deque, or null if empty */ public T peekLast() { - return tail.val; + return tail != null ? tail.val : null; } /** @@ -203,7 +147,7 @@ public class Deques { * @return whether or not the deque is empty */ public boolean isEmpty() { - return head == null; + return size == 0; } /** @@ -216,25 +160,21 @@ public class Deques { */ @Override public String toString() { - String dequeString = "Head -> "; + StringBuilder dequeString = new StringBuilder("Head -> "); DequeNode currNode = head; while (currNode != null) { - dequeString += currNode.val; - + dequeString.append(currNode.val); if (currNode.next != null) { - dequeString += " <-> "; + dequeString.append(" <-> "); } - currNode = currNode.next; } - - dequeString += " <- Tail"; - - return dequeString; + dequeString.append(" <- Tail"); + return dequeString.toString(); } public static void main(String[] args) { - Deques myDeque = new Deques(); + Deque myDeque = new Deque<>(); for (int i = 0; i < 42; i++) { if (i / 42.0 < 0.5) { myDeque.addFirst(i); diff --git a/src/test/java/com/thealgorithms/datastructures/queues/DequeTest.java b/src/test/java/com/thealgorithms/datastructures/queues/DequeTest.java new file mode 100644 index 00000000..1244a2e2 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/queues/DequeTest.java @@ -0,0 +1,90 @@ +package com.thealgorithms.datastructures.queues; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.NoSuchElementException; +import org.junit.jupiter.api.Test; + +class DequeTest { + + @Test + void testAddFirst() { + Deque deque = new Deque<>(); + deque.addFirst(10); + assertEquals(10, deque.peekFirst()); + assertEquals(10, deque.peekLast()); + assertEquals(1, deque.size()); + } + + @Test + void testAddLast() { + Deque deque = new Deque<>(); + deque.addLast(20); + assertEquals(20, deque.peekFirst()); + assertEquals(20, deque.peekLast()); + assertEquals(1, deque.size()); + } + + @Test + void testPollFirst() { + Deque deque = new Deque<>(); + deque.addFirst(10); + deque.addLast(20); + assertEquals(10, deque.pollFirst()); + assertEquals(20, deque.peekFirst()); + assertEquals(1, deque.size()); + } + + @Test + void testPollLast() { + Deque deque = new Deque<>(); + deque.addFirst(10); + deque.addLast(20); + assertEquals(20, deque.pollLast()); + assertEquals(10, deque.peekLast()); + assertEquals(1, deque.size()); + } + + @Test + void testIsEmpty() { + Deque deque = new Deque<>(); + org.junit.jupiter.api.Assertions.assertTrue(deque.isEmpty()); + deque.addFirst(10); + assertFalse(deque.isEmpty()); + } + + @Test + void testPeekFirstEmpty() { + Deque deque = new Deque<>(); + assertNull(deque.peekFirst()); + } + + @Test + void testPeekLastEmpty() { + Deque deque = new Deque<>(); + assertNull(deque.peekLast()); + } + + @Test + void testPollFirstEmpty() { + Deque deque = new Deque<>(); + org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, deque::pollFirst); + } + + @Test + void testPollLastEmpty() { + Deque deque = new Deque<>(); + org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, deque::pollLast); + } + + @Test + void testToString() { + Deque deque = new Deque<>(); + deque.addFirst(10); + deque.addLast(20); + deque.addFirst(5); + assertEquals("Head -> 5 <-> 10 <-> 20 <- Tail", deque.toString()); + } +}