Skip to content

Commit 259db0d

Browse files
feat(datastructures): add SelfOrganizingLinkedList implementation and tests
1 parent 90c0e87 commit 259db0d

2 files changed

Lines changed: 213 additions & 0 deletions

File tree

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package com.thealgorithms.datastructures.lists;
2+
3+
import java.util.Objects;
4+
5+
6+
7+
/**
8+
* Node structure for the generic linked list.
9+
*
10+
* @param <E> the type of element held in this node
11+
*/
12+
class LinkedList<E> {
13+
E value;
14+
LinkedList<E> next;
15+
16+
public LinkedList(E value) {
17+
this.value = value;
18+
this.next = null;
19+
}
20+
}
21+
22+
23+
/**
24+
* A Self-Organizing Linked List implementation using the Move-To-Front (MTF) strategy.
25+
* When an element is searched, it is automatically moved to the head of the list
26+
* to optimize subsequent lookups.
27+
*
28+
* @param <E> the type of elements held in this list
29+
*/
30+
31+
public class SelfOrganizingLinkedList<E>{
32+
private LinkedList<E> head;
33+
private int size;
34+
35+
public SelfOrganizingLinkedList(){
36+
this.size=0;
37+
this.head=null;
38+
}
39+
40+
/**
41+
* Inserts a new value at the end of the list.
42+
*
43+
* @param value the item to insert
44+
*/
45+
46+
public void insert(E value){
47+
LinkedList<E> newNode=new LinkedList<>(value);
48+
if (head == null) {
49+
head = newNode;
50+
} else {
51+
LinkedList<E> temp = head;
52+
while (temp.next != null) {
53+
temp = temp.next;
54+
}
55+
temp.next = newNode;
56+
}
57+
size++;
58+
59+
}
60+
61+
/**
62+
* Searches for a value in the list.
63+
* If found, moves the node to the front (head) of the list.
64+
*
65+
* @param key the value to search for
66+
* @return true if the element is present, false otherwise
67+
*/
68+
public boolean search(E key) {
69+
if (head == null) {
70+
return false;
71+
}
72+
73+
74+
if (Objects.equals(head.value, key)) {
75+
return true;
76+
}
77+
78+
LinkedList<E> prev = null;
79+
LinkedList<E> curr = head;
80+
81+
82+
while (curr != null && !Objects.equals(curr.value, key)) {
83+
prev = curr;
84+
curr = curr.next;
85+
}
86+
87+
88+
if (curr == null) {
89+
return false;
90+
}
91+
92+
93+
prev.next = curr.next;
94+
curr.next = head;
95+
head = curr;
96+
97+
return true;
98+
}
99+
100+
/**
101+
* Gets the current head of the list.
102+
*
103+
* @return value at head, or null if list is empty
104+
*/
105+
public E getHeadValue() {
106+
return head != null ? head.value : null;
107+
}
108+
109+
/**
110+
* Returns the size of the list.
111+
*
112+
* @return number of elements
113+
*/
114+
public int getSize() {
115+
return size;
116+
}
117+
118+
/**
119+
* Returns true if the list contains no elements.
120+
*
121+
* @return true if empty
122+
*/
123+
public boolean isEmpty() {
124+
return head == null;
125+
}
126+
}
127+
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package com.thealgorithms.datastructures.lists;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertNull;
6+
import static org.junit.jupiter.api.Assertions.assertTrue;
7+
import org.junit.jupiter.api.BeforeEach;
8+
import org.junit.jupiter.api.Test;
9+
10+
public class SelfOrganizingLinkedListTest {
11+
private SelfOrganizingLinkedList<Integer> list;
12+
13+
@BeforeEach
14+
void setUp() {
15+
list = new SelfOrganizingLinkedList<>();
16+
}
17+
18+
@Test
19+
void testSearchOnEmptyList() {
20+
assertFalse(list.search(10));
21+
assertNull(list.getHeadValue());
22+
}
23+
24+
@Test
25+
void testSearchElementAtHeadValueDoesNotChangeStructure() {
26+
list.insert(10);
27+
list.insert(20);
28+
list.insert(30);
29+
30+
assertTrue(list.search(10));
31+
assertEquals(10, list.getHeadValue());
32+
}
33+
34+
@Test
35+
void testMoveMiddleElementToFront() {
36+
list.insert(10);
37+
list.insert(20);
38+
list.insert(30);
39+
list.insert(40);
40+
41+
// Search middle element '30'
42+
assertTrue(list.search(30));
43+
44+
// '30' should now be the new head
45+
assertEquals(30, list.getHeadValue());
46+
}
47+
48+
@Test
49+
void testMoveLastElementToFront() {
50+
list.insert(10);
51+
list.insert(20);
52+
list.insert(30);
53+
54+
// Search last element '30'
55+
assertTrue(list.search(30));
56+
57+
assertEquals(30, list.getHeadValue());
58+
}
59+
60+
@Test
61+
void testSearchNonExistentElement() {
62+
list.insert(10);
63+
list.insert(20);
64+
65+
assertFalse(list.search(99));
66+
assertEquals(10, list.getHeadValue()); // Head remains unchanged
67+
}
68+
69+
@Test
70+
void testMultipleSearchesSequentialMoveToFront() {
71+
list.insert(1);
72+
list.insert(2);
73+
list.insert(3);
74+
75+
list.search(2); // Head becomes 2
76+
assertEquals(2, list.getHeadValue());
77+
78+
list.search(3); // Head becomes 3
79+
assertEquals(3, list.getHeadValue());
80+
81+
list.search(1); // Head becomes 1
82+
assertEquals(1, list.getHeadValue());
83+
}
84+
85+
86+
}

0 commit comments

Comments
 (0)