refactor: Deque (#5353)

This commit is contained in:
Alex Klymenko 2024-08-21 12:39:01 +02:00 committed by GitHub
parent e756a7d2d5
commit a03353d3d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 119 additions and 89 deletions

View File

@ -1,5 +1,7 @@
package com.thealgorithms.datastructures.queues; package com.thealgorithms.datastructures.queues;
import java.util.NoSuchElementException;
/** /**
* A [deque](https://en.wikipedia.org/wiki/Double-ended_queue) is short for a * 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 * 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) * @author [Ian Cowan](https://github.com/iccowan)
*/ */
public class Deques<T> { public class Deque<T> {
/** /**
* Node for the deque * Node for the deque
*/ */
class DequeNode<S> { private static class DequeNode<S> {
/**
* Value of the node
*/
S val; S val;
/**
* Next node in the deque from this node
*/
DequeNode<S> next = null; DequeNode<S> next = null;
/**
* Previous node in the deque from this node
*/
DequeNode<S> prev = null; DequeNode<S> prev = null;
/**
* Constructor
*/
DequeNode(S val) { DequeNode(S val) {
this.val = val; this.val = val;
} }
} }
/** private DequeNode<T> head = null;
* Head of the deque private DequeNode<T> tail = null;
*/ private int size = 0;
DequeNode<T> head = null;
/**
* Tail of the deque
*/
DequeNode<T> tail = null;
/**
* Size of the deque
*/
int size = 0;
/** /**
* Adds the specified value to the head of the deque * Adds the specified value to the head of the deque
@ -60,16 +36,12 @@ public class Deques<T> {
* @param val Value to add to the deque * @param val Value to add to the deque
*/ */
public void addFirst(T val) { public void addFirst(T val) {
// Create a new node with the given value DequeNode<T> newNode = new DequeNode<>(val);
DequeNode<T> newNode = new DequeNode<T>(val);
// Add the node if (isEmpty()) {
if (head == null) {
// If the deque is empty, add the node as the head and tail
head = newNode; head = newNode;
tail = newNode; tail = newNode;
} else { } else {
// If the deque is not empty, insert the node as the new head
newNode.next = head; newNode.next = head;
head.prev = newNode; head.prev = newNode;
head = newNode; head = newNode;
@ -84,20 +56,15 @@ public class Deques<T> {
* @param val Value to add to the deque * @param val Value to add to the deque
*/ */
public void addLast(T val) { public void addLast(T val) {
// Create a new node with the given value DequeNode<T> newNode = new DequeNode<>(val);
DequeNode<T> newNode = new DequeNode<T>(val);
// Add the node
if (tail == null) { if (tail == null) {
// If the deque is empty, add the node as the head and tail
head = newNode; head = newNode;
tail = newNode;
} else { } else {
// If the deque is not empty, insert the node as the new tail
newNode.prev = tail; newNode.prev = tail;
tail.next = newNode; tail.next = newNode;
}
tail = newNode; tail = newNode;
}
size++; size++;
} }
@ -105,33 +72,21 @@ public class Deques<T> {
* Removes and returns the first (head) value in the deque * Removes and returns the first (head) value in the deque
* *
* @return the value of the head of the deque * @return the value of the head of the deque
* @throws NoSuchElementException if the deque is empty
*/ */
public T pollFirst() { public T pollFirst() {
// If the head is null, return null
if (head == 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; T oldHeadVal = head.val;
// Now, let's remove the head
if (head == tail) { if (head == tail) {
// If there is only one node, remove it
head = null; head = null;
tail = null; tail = null;
} else { } else {
// If there is more than one node, fix the references
head.next.prev = null;
DequeNode<T> oldHead = head;
head = head.next; head = head.next;
head.prev = null;
// Can be considered unnecessary...
// Unlinking the old head to make sure there are no random
// references possibly affecting garbage collection
oldHead.next = null;
} }
size--; size--;
return oldHeadVal; return oldHeadVal;
} }
@ -140,32 +95,21 @@ public class Deques<T> {
* Removes and returns the last (tail) value in the deque * Removes and returns the last (tail) value in the deque
* *
* @return the value of the tail of the deque * @return the value of the tail of the deque
* @throws NoSuchElementException if the deque is empty
*/ */
public T pollLast() { public T pollLast() {
// If the tail is null, return null
if (tail == 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; T oldTailVal = tail.val;
// Now, remove the tail
if (head == tail) { if (head == tail) {
// If there is only one node, remove it
head = null; head = null;
tail = null; tail = null;
} else { } else {
// If there is more than one node, fix the references
tail.prev.next = null;
DequeNode<T> oldTail = tail;
tail = tail.prev; tail = tail.prev;
tail.next = null;
// Similarly to above, can be considered unnecessary
// See `pollFirst()` for explanation
oldTail.prev = null;
} }
size--; size--;
return oldTailVal; return oldTailVal;
} }
@ -173,19 +117,19 @@ public class Deques<T> {
/** /**
* Returns the first (head) value of the deque WITHOUT removing * 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() { public T peekFirst() {
return head.val; return head != null ? head.val : null;
} }
/** /**
* Returns the last (tail) value of the deque WITHOUT removing * 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() { public T peekLast() {
return tail.val; return tail != null ? tail.val : null;
} }
/** /**
@ -203,7 +147,7 @@ public class Deques<T> {
* @return whether or not the deque is empty * @return whether or not the deque is empty
*/ */
public boolean isEmpty() { public boolean isEmpty() {
return head == null; return size == 0;
} }
/** /**
@ -216,25 +160,21 @@ public class Deques<T> {
*/ */
@Override @Override
public String toString() { public String toString() {
String dequeString = "Head -> "; StringBuilder dequeString = new StringBuilder("Head -> ");
DequeNode<T> currNode = head; DequeNode<T> currNode = head;
while (currNode != null) { while (currNode != null) {
dequeString += currNode.val; dequeString.append(currNode.val);
if (currNode.next != null) { if (currNode.next != null) {
dequeString += " <-> "; dequeString.append(" <-> ");
} }
currNode = currNode.next; currNode = currNode.next;
} }
dequeString.append(" <- Tail");
dequeString += " <- Tail"; return dequeString.toString();
return dequeString;
} }
public static void main(String[] args) { public static void main(String[] args) {
Deques<Integer> myDeque = new Deques<Integer>(); Deque<Integer> myDeque = new Deque<>();
for (int i = 0; i < 42; i++) { for (int i = 0; i < 42; i++) {
if (i / 42.0 < 0.5) { if (i / 42.0 < 0.5) {
myDeque.addFirst(i); myDeque.addFirst(i);

View File

@ -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<Integer> deque = new Deque<>();
deque.addFirst(10);
assertEquals(10, deque.peekFirst());
assertEquals(10, deque.peekLast());
assertEquals(1, deque.size());
}
@Test
void testAddLast() {
Deque<Integer> deque = new Deque<>();
deque.addLast(20);
assertEquals(20, deque.peekFirst());
assertEquals(20, deque.peekLast());
assertEquals(1, deque.size());
}
@Test
void testPollFirst() {
Deque<Integer> 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<Integer> 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<Integer> deque = new Deque<>();
org.junit.jupiter.api.Assertions.assertTrue(deque.isEmpty());
deque.addFirst(10);
assertFalse(deque.isEmpty());
}
@Test
void testPeekFirstEmpty() {
Deque<Integer> deque = new Deque<>();
assertNull(deque.peekFirst());
}
@Test
void testPeekLastEmpty() {
Deque<Integer> deque = new Deque<>();
assertNull(deque.peekLast());
}
@Test
void testPollFirstEmpty() {
Deque<Integer> deque = new Deque<>();
org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, deque::pollFirst);
}
@Test
void testPollLastEmpty() {
Deque<Integer> deque = new Deque<>();
org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, deque::pollLast);
}
@Test
void testToString() {
Deque<Integer> deque = new Deque<>();
deque.addFirst(10);
deque.addLast(20);
deque.addFirst(5);
assertEquals("Head -> 5 <-> 10 <-> 20 <- Tail", deque.toString());
}
}