mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-05 16:27:33 +08:00
Enhance docs, add tests in CursorLinkedList
(#5994)
This commit is contained in:
@ -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)
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user