Enhance docs, add tests in CursorLinkedList (#5994)

This commit is contained in:
Hardik Pawar
2024-10-25 20:31:06 +05:30
committed by GitHub
parent 3a8b04afea
commit f3e0900d2b
3 changed files with 197 additions and 60 deletions

View File

@ -830,6 +830,7 @@
* lists * lists
* [CircleLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java) * [CircleLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java)
* [CreateAndDetectLoopTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoopTest.java) * [CreateAndDetectLoopTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoopTest.java)
* [CursorLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CursorLinkedListTest.java)
* [MergeKSortedLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedListTest.java) * [MergeKSortedLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedListTest.java)
* [MergeSortedArrayListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedArrayListTest.java) * [MergeSortedArrayListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedArrayListTest.java)
* [MergeSortedSinglyLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedListTest.java) * [MergeSortedSinglyLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedListTest.java)

View File

@ -3,18 +3,20 @@ package com.thealgorithms.datastructures.lists;
import java.util.Objects; import java.util.Objects;
/** /**
* This class implements a Cursor Linked List. * CursorLinkedList is an array-based implementation of a singly linked list.
* * Each node in the array simulates a linked list node, storing an element and
* A CursorLinkedList is an array version of a Linked List. Essentially you have * the index of the next node. This structure allows for efficient list operations
* an array of list nodes but instead of each node containing a pointer to the * without relying on traditional pointers.
* next item in the linked list, each node element in the array contains the
* index for the next node element.
* *
* @param <T> the type of elements in this list
*/ */
public class CursorLinkedList<T> { public class CursorLinkedList<T> {
/**
* Node represents an individual element in the list, containing the element
* itself and a pointer (index) to the next node.
*/
private static class Node<T> { private static class Node<T> {
T element; T element;
int next; int next;
@ -31,7 +33,7 @@ public class CursorLinkedList<T> {
private static final int CURSOR_SPACE_SIZE = 100; private static final int CURSOR_SPACE_SIZE = 100;
{ {
// init at loading time // Initialize cursor space array and free list pointers
cursorSpace = new Node[CURSOR_SPACE_SIZE]; cursorSpace = new Node[CURSOR_SPACE_SIZE];
for (int i = 0; i < CURSOR_SPACE_SIZE; i++) { for (int i = 0; i < CURSOR_SPACE_SIZE; i++) {
cursorSpace[i] = new Node<>(null, i + 1); cursorSpace[i] = new Node<>(null, i + 1);
@ -39,12 +41,18 @@ public class CursorLinkedList<T> {
cursorSpace[CURSOR_SPACE_SIZE - 1].next = 0; cursorSpace[CURSOR_SPACE_SIZE - 1].next = 0;
} }
/**
* Constructs an empty CursorLinkedList with the default capacity.
*/
public CursorLinkedList() { public CursorLinkedList() {
os = 0; os = 0;
count = 0; count = 0;
head = -1; head = -1;
} }
/**
* Prints all elements in the list in their current order.
*/
public void printList() { public void printList() {
if (head != -1) { if (head != -1) {
int start = head; int start = head;
@ -57,27 +65,36 @@ public class CursorLinkedList<T> {
} }
/** /**
* @return the logical index of the element within the list , not the actual * Finds the logical index of a specified element in the list.
* index of the [cursorSpace] array *
* @param element the element to search for in the list
* @return the logical index of the element, or -1 if not found
* @throws NullPointerException if element is null
*/ */
public int indexOf(T element) { public int indexOf(T element) {
Objects.requireNonNull(element); if (element == null) {
Node<T> iterator = cursorSpace[head]; throw new NullPointerException("Element cannot be null");
for (int i = 0; i < count; i++) { }
if (iterator.element.equals(element)) { try {
return i; Objects.requireNonNull(element);
} Node<T> iterator = cursorSpace[head];
iterator = cursorSpace[iterator.next]; for (int i = 0; i < count; i++) {
if (iterator.element.equals(element)) {
return i;
}
iterator = cursorSpace[iterator.next];
}
} catch (Exception e) {
return -1;
} }
return -1; return -1;
} }
/** /**
* @param position , the logical index of the element , not the actual one * Retrieves an element at a specified logical index in the list.
* within the [cursorSpace] array . this method should be used to get the *
* index give by indexOf() method. * @param position the logical index of the element
* @return * @return the element at the specified position, or null if index is out of bounds
*/ */
public T get(int position) { public T get(int position) {
if (position >= 0 && position < count) { if (position >= 0 && position < count) {
@ -88,15 +105,18 @@ public class CursorLinkedList<T> {
if (counter == position) { if (counter == position) {
return element; return element;
} }
start = cursorSpace[start].next; start = cursorSpace[start].next;
counter++; counter++;
} }
} }
return null; return null;
} }
/**
* Removes the element at a specified logical index from the list.
*
* @param index the logical index of the element to remove
*/
public void removeByIndex(int index) { public void removeByIndex(int index) {
if (index >= 0 && index < count) { if (index >= 0 && index < count) {
T element = get(index); T element = get(index);
@ -104,19 +124,22 @@ public class CursorLinkedList<T> {
} }
} }
/**
* Removes a specified element from the list.
*
* @param element the element to be removed
* @throws NullPointerException if element is null
*/
public void remove(T element) { public void remove(T element) {
Objects.requireNonNull(element); Objects.requireNonNull(element);
// case element is in the head
T tempElement = cursorSpace[head].element; T tempElement = cursorSpace[head].element;
int tempNext = cursorSpace[head].next; int tempNext = cursorSpace[head].next;
if (tempElement.equals(element)) { if (tempElement.equals(element)) {
free(head); free(head);
head = tempNext; head = tempNext;
} else { // otherwise cases } else {
int prevIndex = head; int prevIndex = head;
int currentIndex = cursorSpace[prevIndex].next; int currentIndex = cursorSpace[prevIndex].next;
while (currentIndex != -1) { while (currentIndex != -1) {
T currentElement = cursorSpace[currentIndex].element; T currentElement = cursorSpace[currentIndex].element;
if (currentElement.equals(element)) { if (currentElement.equals(element)) {
@ -124,15 +147,34 @@ public class CursorLinkedList<T> {
free(currentIndex); free(currentIndex);
break; break;
} }
prevIndex = currentIndex; prevIndex = currentIndex;
currentIndex = cursorSpace[prevIndex].next; currentIndex = cursorSpace[prevIndex].next;
} }
} }
count--; count--;
} }
/**
* Allocates a new node index for storing an element.
*
* @return the index of the newly allocated node
* @throws OutOfMemoryError if no space is available in cursor space
*/
private int alloc() {
int availableNodeIndex = cursorSpace[os].next;
if (availableNodeIndex == 0) {
throw new OutOfMemoryError();
}
cursorSpace[os].next = cursorSpace[availableNodeIndex].next;
cursorSpace[availableNodeIndex].next = -1;
return availableNodeIndex;
}
/**
* Releases a node back to the free list.
*
* @param index the index of the node to release
*/
private void free(int index) { private void free(int index) {
Node<T> osNode = cursorSpace[os]; Node<T> osNode = cursorSpace[os];
int osNext = osNode.next; int osNext = osNode.next;
@ -141,44 +183,26 @@ public class CursorLinkedList<T> {
cursorSpace[index].next = osNext; cursorSpace[index].next = osNext;
} }
/**
* Appends an element to the end of the list.
*
* @param element the element to append
* @throws NullPointerException if element is null
*/
public void append(T element) { public void append(T element) {
Objects.requireNonNull(element); Objects.requireNonNull(element);
int availableIndex = alloc(); int availableIndex = alloc();
cursorSpace[availableIndex].element = element; cursorSpace[availableIndex].element = element;
if (head == -1) { if (head == -1) {
head = availableIndex; head = availableIndex;
} else {
int iterator = head;
while (cursorSpace[iterator].next != -1) {
iterator = cursorSpace[iterator].next;
}
cursorSpace[iterator].next = availableIndex;
} }
int iterator = head;
while (cursorSpace[iterator].next != -1) {
iterator = cursorSpace[iterator].next;
}
cursorSpace[iterator].next = availableIndex;
cursorSpace[availableIndex].next = -1; cursorSpace[availableIndex].next = -1;
count++; count++;
} }
/**
* @return the index of the next available node
*/
private int alloc() {
// 1- get the index at which the os is pointing
int availableNodeIndex = cursorSpace[os].next;
if (availableNodeIndex == 0) {
throw new OutOfMemoryError();
}
// 2- make the os point to the next of the @var{availableNodeIndex}
cursorSpace[os].next = cursorSpace[availableNodeIndex].next;
// this to indicate an end of the list , helpful at testing since any err
// would throw an outOfBoundException
cursorSpace[availableNodeIndex].next = -1;
return availableNodeIndex;
}
} }

View File

@ -0,0 +1,112 @@
package com.thealgorithms.datastructures.lists;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class CursorLinkedListTest {
private CursorLinkedList<String> list;
@BeforeEach
void setUp() {
list = new CursorLinkedList<>();
}
@Test
void testAppendAndGet() {
list.append("First");
list.append("Second");
list.append("Third");
assertEquals("First", list.get(0));
assertEquals("Second", list.get(1));
assertEquals("Third", list.get(2));
assertNull(list.get(3));
assertNull(list.get(-1));
}
@Test
void testIndexOf() {
list.append("First");
list.append("Second");
list.append("Third");
assertEquals(0, list.indexOf("First"));
assertEquals(1, list.indexOf("Second"));
assertEquals(2, list.indexOf("Third"));
assertEquals(-1, list.indexOf("NonExistent"));
}
@Test
void testRemove() {
list.append("First");
list.append("Second");
list.append("Third");
list.remove("Second");
assertEquals("First", list.get(0));
assertEquals("Third", list.get(1));
assertNull(list.get(2));
assertEquals(-1, list.indexOf("Second"));
}
@Test
void testRemoveByIndex() {
list.append("First");
list.append("Second");
list.append("Third");
list.removeByIndex(1);
assertEquals("First", list.get(0));
assertEquals("Third", list.get(1));
assertNull(list.get(2));
}
@Test
void testRemoveFirstElement() {
list.append("First");
list.append("Second");
list.remove("First");
assertEquals("Second", list.get(0));
assertNull(list.get(1));
assertEquals(-1, list.indexOf("First"));
}
@Test
void testRemoveLastElement() {
list.append("First");
list.append("Second");
list.remove("Second");
assertEquals("First", list.get(0));
assertNull(list.get(1));
assertEquals(-1, list.indexOf("Second"));
}
@Test
void testNullHandling() {
assertThrows(NullPointerException.class, () -> list.append(null));
assertThrows(NullPointerException.class, () -> list.remove(null));
assertThrows(NullPointerException.class, () -> list.indexOf(null));
}
@Test
void testEmptyList() {
assertNull(list.get(0));
assertEquals(-1, list.indexOf("Any"));
}
@Test
void testMemoryLimitExceeded() {
// Test adding more elements than CURSOR_SPACE_SIZE
assertThrows(OutOfMemoryError.class, () -> {
for (int i = 0; i < 101; i++) { // CURSOR_SPACE_SIZE is 100
list.append("Element" + i);
}
});
}
}