diff --git a/src/main/java/com/thealgorithms/datastructures/lists/CircleLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/CircleLinkedList.java index 2b50f73101fb..422e8953625f 100644 --- a/src/main/java/com/thealgorithms/datastructures/lists/CircleLinkedList.java +++ b/src/main/java/com/thealgorithms/datastructures/lists/CircleLinkedList.java @@ -1,8 +1,23 @@ package com.thealgorithms.datastructures.lists; +/** + * This class is a circular singly linked list implementation. In a circular linked list, + * the last node points back to the first node, creating a circular chain. + * + *

This implementation includes basic operations such as appending elements + * to the end, removing elements from a specified position, and converting + * the list to a string representation. + * + * @param the type of elements held in this list + */ public class CircleLinkedList { - private static final class Node { + /** + * A static nested class representing a node in the circular linked list. + * + * @param the type of element stored in the node + */ + static final class Node { Node next; E value; @@ -13,44 +28,56 @@ private Node(E value, Node next) { } } - // For better O.O design this should be private allows for better black box design private int size; - // this will point to dummy node; - private Node head = null; - private Node tail = null; // keeping a tail pointer to keep track of the end of list + Node head = null; + private Node tail; - // constructor for class.. here we will make a dummy node for circly linked list implementation - // with reduced error catching as our list will never be empty; + /** + * Initializes a new circular linked list. A dummy head node is used for simplicity, + * pointing initially to itself to ensure the list is never empty. + */ public CircleLinkedList() { - // creation of the dummy node - head = new Node(null, head); + head = new Node<>(null, head); tail = head; size = 0; } - // getter for the size... needed because size is private. + /** + * Returns the current size of the list. + * + * @return the number of elements in the list + */ public int getSize() { return size; } - // for the sake of simplistiy this class will only contain the append function or addLast other - // add functions can be implemented however this is the basses of them all really. + /** + * Appends a new element to the end of the list. Throws a NullPointerException if + * a null value is provided. + * + * @param value the value to append to the list + * @throws NullPointerException if the value is null + */ public void append(E value) { if (value == null) { - // we do not want to add null elements to the list. throw new NullPointerException("Cannot add null element to the list"); } - // head.next points to the last element; if (tail == null) { - tail = new Node(value, head); + tail = new Node<>(value, head); head.next = tail; } else { - tail.next = new Node(value, head); + tail.next = new Node<>(value, head); tail = tail.next; } size++; } + /** + * Returns a string representation of the list in the format "[ element1, element2, ... ]". + * An empty list is represented as "[]". + * + * @return the string representation of the list + */ public String toString() { if (size == 0) { return "[]"; @@ -68,23 +95,27 @@ public String toString() { return sb.toString(); } + /** + * Removes and returns the element at the specified position in the list. + * Throws an IndexOutOfBoundsException if the position is invalid. + * + * @param pos the position of the element to remove + * @return the value of the removed element + * @throws IndexOutOfBoundsException if the position is out of range + */ public E remove(int pos) { if (pos >= size || pos < 0) { - // catching errors - throw new IndexOutOfBoundsException("position cannot be greater than size or negative"); + throw new IndexOutOfBoundsException("Position out of bounds"); } - // we need to keep track of the element before the element we want to remove we can see why - // bellow. + Node before = head; for (int i = 1; i <= pos; i++) { before = before.next; } Node destroy = before.next; E saved = destroy.value; - // assigning the next reference to the element following the element we want to remove... - // the last element will be assigned to the head. - before.next = before.next.next; - // scrubbing + before.next = destroy.next; + if (destroy == tail) { tail = before; } diff --git a/src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java index b7a05ef29d66..883d2b02ba7c 100644 --- a/src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java +++ b/src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java @@ -1,78 +1,115 @@ package com.thealgorithms.datastructures.lists; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class CircleLinkedListTest { + private CircleLinkedList list; + + @BeforeEach + public void setUp() { + list = new CircleLinkedList<>(); + } + + @Test + public void testInitialSize() { + assertEquals(0, list.getSize(), "Initial size should be 0."); + } + @Test public void testAppendAndSize() { - CircleLinkedList list = new CircleLinkedList<>(); list.append(1); list.append(2); list.append(3); - assertEquals(3, list.getSize()); - assertEquals("[ 1, 2, 3 ]", list.toString()); + assertEquals(3, list.getSize(), "Size after three appends should be 3."); + assertEquals("[ 1, 2, 3 ]", list.toString(), "List content should match appended values."); } @Test public void testRemove() { - CircleLinkedList list = new CircleLinkedList<>(); list.append(1); list.append(2); list.append(3); list.append(4); - assertEquals(2, list.remove(1)); - assertEquals(3, list.remove(1)); - assertEquals("[ 1, 4 ]", list.toString()); - assertEquals(2, list.getSize()); + assertEquals(2, list.remove(1), "Removed element at index 1 should be 2."); + assertEquals(3, list.remove(1), "Removed element at index 1 after update should be 3."); + assertEquals("[ 1, 4 ]", list.toString(), "List content should reflect removals."); + assertEquals(2, list.getSize(), "Size after two removals should be 2."); } @Test public void testRemoveInvalidIndex() { - CircleLinkedList list = new CircleLinkedList<>(); list.append(1); list.append(2); - assertThrows(IndexOutOfBoundsException.class, () -> list.remove(2)); - assertThrows(IndexOutOfBoundsException.class, () -> list.remove(-1)); + assertThrows(IndexOutOfBoundsException.class, () -> list.remove(2), "Should throw on out-of-bounds index."); + assertThrows(IndexOutOfBoundsException.class, () -> list.remove(-1), "Should throw on negative index."); } @Test public void testToStringEmpty() { - CircleLinkedList list = new CircleLinkedList<>(); - assertEquals("[]", list.toString()); + assertEquals("[]", list.toString(), "Empty list should be represented by '[]'."); } @Test public void testToStringAfterRemoval() { - CircleLinkedList list = new CircleLinkedList<>(); list.append(1); list.append(2); list.append(3); list.remove(1); - assertEquals("[ 1, 3 ]", list.toString()); + assertEquals("[ 1, 3 ]", list.toString(), "List content should match remaining elements after removal."); } @Test public void testSingleElement() { - CircleLinkedList list = new CircleLinkedList<>(); list.append(1); - assertEquals(1, list.getSize()); - assertEquals("[ 1 ]", list.toString()); - assertEquals(1, list.remove(0)); - assertEquals("[]", list.toString()); + assertEquals(1, list.getSize(), "Size after single append should be 1."); + assertEquals("[ 1 ]", list.toString(), "Single element list should display properly."); + assertEquals(1, list.remove(0), "Single element removed should match appended value."); + assertEquals("[]", list.toString(), "List should be empty after removing the single element."); } @Test public void testNullElement() { - CircleLinkedList list = new CircleLinkedList<>(); - assertThrows(NullPointerException.class, () -> list.append(null)); + assertThrows(NullPointerException.class, () -> list.append(null), "Appending null should throw exception."); + } + + @Test + public void testCircularReference() { + list.append(1); + list.append(2); + list.append(3); + CircleLinkedList.Node current = list.head; + + // Traverse one full cycle and verify the circular reference + for (int i = 0; i <= list.getSize(); i++) { + current = current.next; + } + assertEquals(list.head, current, "End of list should point back to the head (circular structure)."); + } + + @Test + public void testClear() { + list.append(1); + list.append(2); + list.append(3); + + // Remove all elements to simulate clearing the list + for (int i = list.getSize() - 1; i >= 0; i--) { + list.remove(i); + } + + assertEquals(0, list.getSize(), "Size after clearing should be 0."); + assertEquals("[]", list.toString(), "Empty list should be represented by '[]' after clear."); + assertSame(list.head.next, list.head, "Head's next should point to itself after clearing."); } }