Skip to content

Commit b50d1d0

Browse files
authored
Added Sentinel Linear Search . (#6671)
* Added Sentinel Linear Search . * Fixed linter error * Fixed linter errors * Fixed linter errors in test fike * Fixed linter errors in test file * Fixed linter errors
1 parent b316dcf commit b50d1d0

File tree

2 files changed

+324
-0
lines changed

2 files changed

+324
-0
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.thealgorithms.searches;
2+
3+
import com.thealgorithms.devutils.searches.SearchAlgorithm;
4+
5+
/**
6+
* Sentinel Linear Search is a variation of linear search that eliminates the
7+
* need to check the array bounds in each iteration by placing the search key
8+
* at the end of the array as a sentinel value.
9+
*
10+
* <p>
11+
* The algorithm works by:
12+
* 1. Storing the last element of the array
13+
* 2. Placing the search key at the last position (sentinel)
14+
* 3. Searching from the beginning without bound checking
15+
* 4. If found before the last position, return the index
16+
* 5. If found at the last position, check if it was originally there
17+
*
18+
* <p>
19+
* Time Complexity:
20+
* - Best case: O(1) - when the element is at the first position
21+
* - Average case: O(n) - when the element is in the middle
22+
* - Worst case: O(n) - when the element is not present
23+
*
24+
* <p>
25+
* Space Complexity: O(1) - only uses constant extra space
26+
*
27+
* <p>
28+
* Advantages over regular linear search:
29+
* - Reduces the number of comparisons by eliminating bound checking
30+
* - Slightly more efficient in practice due to fewer conditional checks
31+
*
32+
* @author TheAlgorithms Contributors
33+
* @see LinearSearch
34+
* @see SearchAlgorithm
35+
*/
36+
public class SentinelLinearSearch implements SearchAlgorithm {
37+
/**
38+
* Performs sentinel linear search on the given array.
39+
*
40+
* @param array the array to search in
41+
* @param key the element to search for
42+
* @param <T> the type of elements in the array, must be Comparable
43+
* @return the index of the first occurrence of the key, or -1 if not found
44+
* @throws IllegalArgumentException if the array is null
45+
*/
46+
@Override
47+
public <T extends Comparable<T>> int find(T[] array, T key) {
48+
if (array == null) {
49+
throw new IllegalArgumentException("Array cannot be null");
50+
}
51+
52+
if (array.length == 0) {
53+
return -1;
54+
}
55+
56+
if (key == null) {
57+
return findNull(array);
58+
}
59+
60+
// Store the last element
61+
T lastElement = array[array.length - 1];
62+
63+
// Place the sentinel (search key) at the end
64+
array[array.length - 1] = key;
65+
66+
int i = 0;
67+
// Search without bound checking since sentinel guarantees we'll find the key
68+
while (array[i].compareTo(key) != 0) {
69+
i++;
70+
}
71+
72+
// Restore the original last element
73+
array[array.length - 1] = lastElement;
74+
75+
// Check if we found the key before the sentinel position
76+
// or if the original last element was the key we were looking for
77+
if (i < array.length - 1 || (lastElement != null && lastElement.compareTo(key) == 0)) {
78+
return i;
79+
}
80+
81+
return -1; // Key not found
82+
}
83+
84+
/**
85+
* Helper method to find null values in the array.
86+
*
87+
* @param array the array to search in
88+
* @param <T> the type of elements in the array
89+
* @return the index of the first null element, or -1 if not found
90+
*/
91+
private <T extends Comparable<T>> int findNull(T[] array) {
92+
for (int i = 0; i < array.length; i++) {
93+
if (array[i] == null) {
94+
return i;
95+
}
96+
}
97+
return -1;
98+
}
99+
}
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
package com.thealgorithms.searches;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import java.util.Random;
7+
import org.junit.jupiter.api.Test;
8+
9+
/**
10+
* Unit tests for the SentinelLinearSearch class.
11+
*/
12+
class SentinelLinearSearchTest {
13+
14+
/**
15+
* Test for finding an element present in the array.
16+
*/
17+
@Test
18+
void testSentinelLinearSearchFound() {
19+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
20+
Integer[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
21+
Integer key = 5; // Element to find
22+
assertEquals(5, sentinelLinearSearch.find(array, key), "The index of the found element should be 5.");
23+
}
24+
25+
/**
26+
* Test for finding the first element in the array.
27+
*/
28+
@Test
29+
void testSentinelLinearSearchFirstElement() {
30+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
31+
Integer[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
32+
Integer key = 0; // First element
33+
assertEquals(0, sentinelLinearSearch.find(array, key), "The index of the first element should be 0.");
34+
}
35+
36+
/**
37+
* Test for finding the last element in the array.
38+
*/
39+
@Test
40+
void testSentinelLinearSearchLastElement() {
41+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
42+
Integer[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
43+
Integer key = 10; // Last element
44+
assertEquals(10, sentinelLinearSearch.find(array, key), "The index of the last element should be 10.");
45+
}
46+
47+
/**
48+
* Test for finding an element not present in the array.
49+
*/
50+
@Test
51+
void testSentinelLinearSearchNotFound() {
52+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
53+
Integer[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
54+
Integer key = -1; // Element not in the array
55+
assertEquals(-1, sentinelLinearSearch.find(array, key), "The element should not be found in the array.");
56+
}
57+
58+
/**
59+
* Test for finding an element in an empty array.
60+
*/
61+
@Test
62+
void testSentinelLinearSearchEmptyArray() {
63+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
64+
Integer[] array = {}; // Empty array
65+
Integer key = 1; // Key not present
66+
assertEquals(-1, sentinelLinearSearch.find(array, key), "The element should not be found in an empty array.");
67+
}
68+
69+
/**
70+
* Test for finding an element in a single-element array when present.
71+
*/
72+
@Test
73+
void testSentinelLinearSearchSingleElementFound() {
74+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
75+
Integer[] array = {42}; // Single element array
76+
Integer key = 42; // Element present
77+
assertEquals(0, sentinelLinearSearch.find(array, key), "The element should be found at index 0.");
78+
}
79+
80+
/**
81+
* Test for finding an element in a single-element array when not present.
82+
*/
83+
@Test
84+
void testSentinelLinearSearchSingleElementNotFound() {
85+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
86+
Integer[] array = {42}; // Single element array
87+
Integer key = 24; // Element not present
88+
assertEquals(-1, sentinelLinearSearch.find(array, key), "The element should not be found in the array.");
89+
}
90+
91+
/**
92+
* Test for finding multiple occurrences of the same element in the array.
93+
*/
94+
@Test
95+
void testSentinelLinearSearchMultipleOccurrences() {
96+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
97+
Integer[] array = {1, 2, 3, 4, 5, 3, 6, 7, 3}; // 3 occurs multiple times
98+
Integer key = 3; // Key to find
99+
assertEquals(2, sentinelLinearSearch.find(array, key), "The index of the first occurrence of the element should be 2.");
100+
}
101+
102+
/**
103+
* Test for finding an element in a large array.
104+
*/
105+
@Test
106+
void testSentinelLinearSearchLargeArray() {
107+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
108+
Integer[] array = new Integer[1000];
109+
for (int i = 0; i < array.length; i++) {
110+
array[i] = i; // Fill the array with integers 0 to 999
111+
}
112+
Integer key = 256; // Present in the array
113+
assertEquals(256, sentinelLinearSearch.find(array, key), "The index of the found element should be 256.");
114+
}
115+
116+
/**
117+
* Test for finding an element in a large array when it is not present.
118+
*/
119+
@Test
120+
void testSentinelLinearSearchLargeArrayNotFound() {
121+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
122+
Integer[] array = new Integer[1000];
123+
for (int i = 0; i < array.length; i++) {
124+
array[i] = i; // Fill the array with integers 0 to 999
125+
}
126+
Integer key = 1001; // Key not present
127+
assertEquals(-1, sentinelLinearSearch.find(array, key), "The element should not be found in the array.");
128+
}
129+
130+
/**
131+
* Test for performance with random large array.
132+
*/
133+
@Test
134+
void testSentinelLinearSearchRandomArray() {
135+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
136+
Random random = new Random();
137+
Integer[] array = random.ints(0, 1000).distinct().limit(1000).boxed().toArray(Integer[] ::new);
138+
Integer key = array[random.nextInt(array.length)]; // Key should be in the array
139+
assertEquals(java.util.Arrays.asList(array).indexOf(key), sentinelLinearSearch.find(array, key), "The index of the found element should match.");
140+
}
141+
142+
/**
143+
* Test for handling null array.
144+
*/
145+
@Test
146+
void testSentinelLinearSearchNullArray() {
147+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
148+
Integer[] array = null; // Null array
149+
Integer key = 1; // Any key
150+
assertThrows(IllegalArgumentException.class, () -> sentinelLinearSearch.find(array, key), "Should throw IllegalArgumentException for null array.");
151+
}
152+
153+
/**
154+
* Test for handling null key in array with null elements.
155+
*/
156+
@Test
157+
void testSentinelLinearSearchNullKey() {
158+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
159+
Integer[] array = {1, null, 3, 4, null}; // Array with null elements
160+
Integer key = null; // Null key
161+
assertEquals(1, sentinelLinearSearch.find(array, key), "The index of the first null element should be 1.");
162+
}
163+
164+
/**
165+
* Test for handling null key when not present in array.
166+
*/
167+
@Test
168+
void testSentinelLinearSearchNullKeyNotFound() {
169+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
170+
Integer[] array = {1, 2, 3, 4, 5}; // Array without null elements
171+
Integer key = null; // Null key
172+
assertEquals(-1, sentinelLinearSearch.find(array, key), "Null key should not be found in array without null elements.");
173+
}
174+
175+
/**
176+
* Test with String array to verify generic functionality.
177+
*/
178+
@Test
179+
void testSentinelLinearSearchStringArray() {
180+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
181+
String[] array = {"apple", "banana", "cherry", "date", "elderberry"};
182+
String key = "cherry"; // Element to find
183+
assertEquals(2, sentinelLinearSearch.find(array, key), "The index of 'cherry' should be 2.");
184+
}
185+
186+
/**
187+
* Test with String array when element not found.
188+
*/
189+
@Test
190+
void testSentinelLinearSearchStringArrayNotFound() {
191+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
192+
String[] array = {"apple", "banana", "cherry", "date", "elderberry"};
193+
String key = "grape"; // Element not in array
194+
assertEquals(-1, sentinelLinearSearch.find(array, key), "The element 'grape' should not be found in the array.");
195+
}
196+
197+
/**
198+
* Test that the original array is not modified after search.
199+
*/
200+
@Test
201+
void testSentinelLinearSearchArrayIntegrity() {
202+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
203+
Integer[] array = {1, 2, 3, 4, 5};
204+
Integer[] originalArray = array.clone(); // Keep a copy of the original
205+
Integer key = 3; // Element to find
206+
207+
sentinelLinearSearch.find(array, key);
208+
209+
// Verify array is unchanged
210+
for (int i = 0; i < array.length; i++) {
211+
assertEquals(originalArray[i], array[i], "Array should remain unchanged after search.");
212+
}
213+
}
214+
215+
/**
216+
* Test edge case where the key is the same as the last element.
217+
*/
218+
@Test
219+
void testSentinelLinearSearchKeyEqualsLastElement() {
220+
SentinelLinearSearch sentinelLinearSearch = new SentinelLinearSearch();
221+
Integer[] array = {1, 2, 3, 4, 5, 3}; // Last element is 3, and 3 also appears earlier
222+
Integer key = 3; // Key equals last element
223+
assertEquals(2, sentinelLinearSearch.find(array, key), "Should find the first occurrence at index 2, not the last.");
224+
}
225+
}

0 commit comments

Comments
 (0)