refactor: LinkedQueue
(#5352)
This commit is contained in:
parent
39ecf70857
commit
5149051e95
@ -2,216 +2,200 @@ package com.thealgorithms.datastructures.queues;
|
|||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.StringJoiner;
|
|
||||||
|
|
||||||
public class LinkedQueue<T> implements Iterable<T> {
|
public class LinkedQueue<T> implements Iterable<T> {
|
||||||
|
|
||||||
static class Node<T> {
|
/**
|
||||||
|
* Node class representing each element in the queue.
|
||||||
|
*/
|
||||||
|
private static class Node<T> {
|
||||||
T data;
|
T data;
|
||||||
Node<T> next;
|
Node<T> next;
|
||||||
|
|
||||||
Node() {
|
|
||||||
this(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
Node(T data) {
|
Node(T data) {
|
||||||
this(data, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
Node(T data, Node<T> next) {
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.next = next;
|
this.next = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private Node<T> front; // Front of the queue
|
||||||
* Front of Queue
|
private Node<T> rear; // Rear of the queue
|
||||||
*/
|
private int size; // Size of the queue
|
||||||
private Node<T> front;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rear of Queue
|
* Initializes an empty LinkedQueue.
|
||||||
*/
|
|
||||||
private Node<T> rear;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Size of Queue
|
|
||||||
*/
|
|
||||||
private int size;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init LinkedQueue
|
|
||||||
*/
|
*/
|
||||||
public LinkedQueue() {
|
public LinkedQueue() {
|
||||||
|
front = null;
|
||||||
front = new Node<>();
|
rear = null;
|
||||||
rear = front;
|
size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if queue is empty
|
* Checks if the queue is empty.
|
||||||
*
|
*
|
||||||
* @return true if queue is empty, otherwise false
|
* @return true if the queue is empty, otherwise false.
|
||||||
*/
|
*/
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return size == 0;
|
return size == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add element to rear of queue
|
* Adds an element to the rear of the queue.
|
||||||
*
|
*
|
||||||
* @param data insert value
|
* @param data the element to insert.
|
||||||
|
* @throws IllegalArgumentException if data is null.
|
||||||
*/
|
*/
|
||||||
public void enqueue(T data) {
|
public void enqueue(T data) {
|
||||||
|
if (data == null) {
|
||||||
|
throw new IllegalArgumentException("Cannot enqueue null data");
|
||||||
|
}
|
||||||
|
|
||||||
Node<T> newNode = new Node<>(data);
|
Node<T> newNode = new Node<>(data);
|
||||||
|
|
||||||
rear.next = newNode;
|
if (isEmpty()) {
|
||||||
|
front = newNode;
|
||||||
|
} else {
|
||||||
|
rear.next = newNode;
|
||||||
|
}
|
||||||
rear = newNode;
|
rear = newNode;
|
||||||
/* make rear point at last node */
|
|
||||||
size++;
|
size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove element at the front of queue
|
* Removes and returns the element at the front of the queue.
|
||||||
*
|
*
|
||||||
* @return element at the front of queue
|
* @return the element at the front of the queue.
|
||||||
|
* @throws NoSuchElementException if the queue is empty.
|
||||||
*/
|
*/
|
||||||
public T dequeue() {
|
public T dequeue() {
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
throw new NoSuchElementException("queue is empty");
|
throw new NoSuchElementException("Queue is empty");
|
||||||
}
|
}
|
||||||
Node<T> destroy = front.next;
|
|
||||||
T retValue = destroy.data;
|
T retValue = front.data;
|
||||||
front.next = front.next.next;
|
front = front.next;
|
||||||
/* clear let GC do it's work */
|
|
||||||
size--;
|
size--;
|
||||||
|
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
front = rear;
|
rear = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return retValue;
|
return retValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Peek element at the front of queue without removing
|
* Returns the element at the front of the queue without removing it.
|
||||||
*
|
*
|
||||||
* @return element at the front
|
* @return the element at the front of the queue.
|
||||||
|
* @throws NoSuchElementException if the queue is empty.
|
||||||
*/
|
*/
|
||||||
public T peekFront() {
|
public T peekFront() {
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
throw new NoSuchElementException("queue is empty");
|
throw new NoSuchElementException("Queue is empty");
|
||||||
}
|
}
|
||||||
return front.next.data;
|
return front.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Peek element at the rear of queue without removing
|
* Returns the element at the rear of the queue without removing it.
|
||||||
*
|
*
|
||||||
* @return element at the front
|
* @return the element at the rear of the queue.
|
||||||
|
* @throws NoSuchElementException if the queue is empty.
|
||||||
*/
|
*/
|
||||||
public T peekRear() {
|
public T peekRear() {
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
throw new NoSuchElementException("queue is empty");
|
throw new NoSuchElementException("Queue is empty");
|
||||||
}
|
}
|
||||||
return rear.data;
|
return rear.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Peeks the element at the index and
|
* Returns the element at the specified position (1-based index).
|
||||||
* returns the value
|
*
|
||||||
* @param pos at which to peek
|
* @param pos the position to peek at (1-based index).
|
||||||
|
* @return the element at the specified position.
|
||||||
|
* @throws IndexOutOfBoundsException if the position is out of range.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public T peek(int pos) {
|
public T peek(int pos) {
|
||||||
if (pos > size) {
|
if (pos < 1 || pos > size) {
|
||||||
throw new IndexOutOfBoundsException("Position %s out of range!".formatted(pos));
|
throw new IndexOutOfBoundsException("Position " + pos + " out of range!");
|
||||||
}
|
}
|
||||||
|
|
||||||
Node<T> node = front;
|
Node<T> node = front;
|
||||||
while (pos-- > 0) {
|
for (int i = 1; i < pos; i++) {
|
||||||
node = node.next;
|
node = node.next;
|
||||||
}
|
}
|
||||||
return node.data;
|
return node.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Node iterator, allows to travel through
|
* Returns an iterator over the elements in the queue.
|
||||||
* the nodes using for() loop or forEach(Consumer)
|
*
|
||||||
|
* @return an iterator over the elements in the queue.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<T> iterator() {
|
public Iterator<T> iterator() {
|
||||||
return new Iterator<>() {
|
return new Iterator<>() {
|
||||||
Node<T> node = front;
|
private Node<T> current = front;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return node.next != null;
|
return current != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T next() {
|
public T next() {
|
||||||
if (hasNext()) {
|
if (!hasNext()) {
|
||||||
node = node.next;
|
throw new NoSuchElementException();
|
||||||
return node.data;
|
|
||||||
}
|
}
|
||||||
throw new NoSuchElementException();
|
|
||||||
|
T data = current.data;
|
||||||
|
current = current.next;
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return size of queue
|
* Returns the size of the queue.
|
||||||
*
|
*
|
||||||
* @return size of queue
|
* @return the size of the queue.
|
||||||
*/
|
*/
|
||||||
public int size() {
|
public int size() {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all nodes in queue
|
* Clears all elements from the queue.
|
||||||
*/
|
*/
|
||||||
public void clear() {
|
public void clear() {
|
||||||
while (size > 0) {
|
front = null;
|
||||||
dequeue();
|
rear = null;
|
||||||
}
|
size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the queue.
|
||||||
|
*
|
||||||
|
* @return a string representation of the queue.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringJoiner join = new StringJoiner(", "); // separator of ', '
|
if (isEmpty()) {
|
||||||
Node<T> travel = front;
|
return "[]";
|
||||||
while ((travel = travel.next) != null) {
|
|
||||||
join.add(String.valueOf(travel.data));
|
|
||||||
}
|
}
|
||||||
return '[' + join.toString() + ']';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Driver Code */
|
StringBuilder sb = new StringBuilder("[");
|
||||||
public static void main(String[] args) {
|
Node<T> current = front;
|
||||||
LinkedQueue<Integer> queue = new LinkedQueue<>();
|
while (current != null) {
|
||||||
assert queue.isEmpty();
|
sb.append(current.data);
|
||||||
|
if (current.next != null) {
|
||||||
queue.enqueue(1);
|
sb.append(", ");
|
||||||
/* 1 */
|
}
|
||||||
queue.enqueue(2);
|
current = current.next;
|
||||||
/* 1 2 */
|
}
|
||||||
queue.enqueue(3);
|
sb.append(']');
|
||||||
/* 1 2 3 */
|
return sb.toString();
|
||||||
System.out.println(queue);
|
|
||||||
/* [1, 2, 3] */
|
|
||||||
|
|
||||||
assert queue.size() == 3;
|
|
||||||
assert queue.dequeue() == 1;
|
|
||||||
assert queue.peekFront() == 2;
|
|
||||||
assert queue.peekRear() == 3;
|
|
||||||
|
|
||||||
queue.clear();
|
|
||||||
assert queue.isEmpty();
|
|
||||||
|
|
||||||
System.out.println(queue);
|
|
||||||
/* [] */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,209 @@
|
|||||||
package com.thealgorithms.datastructures.queues;
|
package com.thealgorithms.datastructures.queues;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
class LinkedQueueTest {
|
class LinkedQueueTest {
|
||||||
|
|
||||||
|
private LinkedQueue<Integer> queue;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
queue = new LinkedQueue<>();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQue() {
|
void testIsEmptyOnNewQueue() {
|
||||||
LinkedQueue<Integer> queue = new LinkedQueue<>();
|
assertTrue(queue.isEmpty(), "Queue should be empty on initialization.");
|
||||||
for (int i = 1; i < 5; i++) {
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEnqueueAndSize() {
|
||||||
|
queue.enqueue(10);
|
||||||
|
assertFalse(queue.isEmpty(), "Queue should not be empty after enqueue.");
|
||||||
|
assertEquals(1, queue.size(), "Queue size should be 1 after one enqueue.");
|
||||||
|
|
||||||
|
queue.enqueue(20);
|
||||||
|
queue.enqueue(30);
|
||||||
|
assertEquals(3, queue.size(), "Queue size should be 3 after three enqueues.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDequeueOnSingleElementQueue() {
|
||||||
|
queue.enqueue(10);
|
||||||
|
assertEquals(10, queue.dequeue(), "Dequeued element should be the same as the enqueued one.");
|
||||||
|
assertTrue(queue.isEmpty(), "Queue should be empty after dequeueing the only element.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDequeueMultipleElements() {
|
||||||
|
queue.enqueue(10);
|
||||||
|
queue.enqueue(20);
|
||||||
|
queue.enqueue(30);
|
||||||
|
|
||||||
|
assertEquals(10, queue.dequeue(), "First dequeued element should be the first enqueued one.");
|
||||||
|
assertEquals(20, queue.dequeue(), "Second dequeued element should be the second enqueued one.");
|
||||||
|
assertEquals(30, queue.dequeue(), "Third dequeued element should be the third enqueued one.");
|
||||||
|
assertTrue(queue.isEmpty(), "Queue should be empty after dequeueing all elements.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDequeueOnEmptyQueue() {
|
||||||
|
org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, () -> queue.dequeue(), "Dequeueing from an empty queue should throw NoSuchElementException.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPeekFrontOnEmptyQueue() {
|
||||||
|
org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, () -> queue.peekFront(), "Peeking front on an empty queue should throw NoSuchElementException.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPeekRearOnEmptyQueue() {
|
||||||
|
org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, () -> queue.peekRear(), "Peeking rear on an empty queue should throw NoSuchElementException.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPeekFront() {
|
||||||
|
queue.enqueue(10);
|
||||||
|
queue.enqueue(20);
|
||||||
|
queue.enqueue(30);
|
||||||
|
|
||||||
|
assertEquals(10, queue.peekFront(), "Peek front should return the first enqueued element.");
|
||||||
|
assertEquals(10, queue.peekFront(), "Peek front should not remove the element.");
|
||||||
|
assertEquals(3, queue.size(), "Queue size should remain unchanged after peek.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPeekRear() {
|
||||||
|
queue.enqueue(10);
|
||||||
|
queue.enqueue(20);
|
||||||
|
queue.enqueue(30);
|
||||||
|
|
||||||
|
assertEquals(30, queue.peekRear(), "Peek rear should return the last enqueued element.");
|
||||||
|
assertEquals(30, queue.peekRear(), "Peek rear should not remove the element.");
|
||||||
|
assertEquals(3, queue.size(), "Queue size should remain unchanged after peek.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPeekAtPosition() {
|
||||||
|
queue.enqueue(10);
|
||||||
|
queue.enqueue(20);
|
||||||
|
queue.enqueue(30);
|
||||||
|
|
||||||
|
assertEquals(10, queue.peek(1), "Peek at position 1 should return the first enqueued element.");
|
||||||
|
assertEquals(20, queue.peek(2), "Peek at position 2 should return the second enqueued element.");
|
||||||
|
assertEquals(30, queue.peek(3), "Peek at position 3 should return the third enqueued element.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPeekAtInvalidPosition() {
|
||||||
|
queue.enqueue(10);
|
||||||
|
queue.enqueue(20);
|
||||||
|
queue.enqueue(30);
|
||||||
|
|
||||||
|
org.junit.jupiter.api.Assertions.assertThrows(IndexOutOfBoundsException.class, () -> queue.peek(4), "Peeking at a position greater than size should throw IndexOutOfBoundsException.");
|
||||||
|
org.junit.jupiter.api.Assertions.assertThrows(IndexOutOfBoundsException.class, () -> queue.peek(0), "Peeking at position 0 should throw IndexOutOfBoundsException.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testClearQueue() {
|
||||||
|
queue.enqueue(10);
|
||||||
|
queue.enqueue(20);
|
||||||
|
queue.enqueue(30);
|
||||||
|
|
||||||
|
queue.clear();
|
||||||
|
assertTrue(queue.isEmpty(), "Queue should be empty after clear.");
|
||||||
|
assertEquals(0, queue.size(), "Queue size should be 0 after clear.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testIterator() {
|
||||||
|
queue.enqueue(10);
|
||||||
|
queue.enqueue(20);
|
||||||
|
queue.enqueue(30);
|
||||||
|
|
||||||
|
Iterator<Integer> it = queue.iterator();
|
||||||
|
assertTrue(it.hasNext(), "Iterator should have next element.");
|
||||||
|
assertEquals(10, it.next(), "First iterator value should be the first enqueued element.");
|
||||||
|
assertEquals(20, it.next(), "Second iterator value should be the second enqueued element.");
|
||||||
|
assertEquals(30, it.next(), "Third iterator value should be the third enqueued element.");
|
||||||
|
assertFalse(it.hasNext(), "Iterator should not have next element after last element.");
|
||||||
|
org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, it::next, "Calling next() on exhausted iterator should throw NoSuchElementException.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testToString() {
|
||||||
|
queue.enqueue(10);
|
||||||
|
queue.enqueue(20);
|
||||||
|
queue.enqueue(30);
|
||||||
|
|
||||||
|
assertEquals("[10, 20, 30]", queue.toString(), "toString should return a properly formatted string representation of the queue.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEnqueueAfterDequeue() {
|
||||||
|
queue.enqueue(10);
|
||||||
|
queue.enqueue(20);
|
||||||
|
queue.enqueue(30);
|
||||||
|
|
||||||
|
assertEquals(10, queue.dequeue(), "Dequeued element should be 10.");
|
||||||
|
assertEquals(20, queue.dequeue(), "Dequeued element should be 20.");
|
||||||
|
|
||||||
|
queue.enqueue(40);
|
||||||
|
assertEquals(30, queue.peekFront(), "Peek front should return 30 after dequeuing and enqueuing new elements.");
|
||||||
|
assertEquals(40, queue.peekRear(), "Peek rear should return 40 after enqueuing new elements.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testQueueMaintainsOrder() {
|
||||||
|
for (int i = 1; i <= 100; i++) {
|
||||||
queue.enqueue(i);
|
queue.enqueue(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(queue.peekRear(), 4);
|
for (int i = 1; i <= 100; i++) {
|
||||||
assertEquals(queue.peek(2), 2);
|
assertEquals(i, queue.dequeue(), "Queue should maintain the correct order of elements.");
|
||||||
|
}
|
||||||
|
|
||||||
assertEquals(queue.peek(4), 4);
|
assertTrue(queue.isEmpty(), "Queue should be empty after dequeuing all elements.");
|
||||||
|
}
|
||||||
|
|
||||||
final int[] element = {1};
|
@Test
|
||||||
|
void testSizeAfterOperations() {
|
||||||
|
assertEquals(0, queue.size(), "Initial queue size should be 0.");
|
||||||
|
|
||||||
// iterates over all the elements present
|
queue.enqueue(10);
|
||||||
// as in the form of nodes
|
assertEquals(1, queue.size(), "Queue size should be 1 after one enqueue.");
|
||||||
queue.forEach(integer -> {
|
|
||||||
if (element[0]++ != integer) {
|
queue.enqueue(20);
|
||||||
throw new AssertionError();
|
assertEquals(2, queue.size(), "Queue size should be 2 after two enqueues.");
|
||||||
}
|
|
||||||
});
|
queue.dequeue();
|
||||||
|
assertEquals(1, queue.size(), "Queue size should be 1 after one dequeue.");
|
||||||
|
|
||||||
|
queue.clear();
|
||||||
|
assertEquals(0, queue.size(), "Queue size should be 0 after clear.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testQueueToStringOnEmptyQueue() {
|
||||||
|
assertEquals("[]", queue.toString(), "toString on empty queue should return '[]'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEnqueueNull() {
|
||||||
|
org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> queue.enqueue(null), "Cannot enqueue null data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testIteratorOnEmptyQueue() {
|
||||||
|
Iterator<Integer> it = queue.iterator();
|
||||||
|
assertFalse(it.hasNext(), "Iterator on empty queue should not have next element.");
|
||||||
|
org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, it::next, "Calling next() on empty queue iterator should throw NoSuchElementException.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user