Skip to content

Commit ff36004

Browse files
fix(searches): refine recursion bounds and add null safety to TernarySearch (#7649)
1 parent f8207cc commit ff36004

2 files changed

Lines changed: 96 additions & 22 deletions

File tree

‎src/main/java/com/thealgorithms/searches/TernarySearch.java‎

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,44 @@
33
import com.thealgorithms.devutils.searches.SearchAlgorithm;
44

55
/**
6-
* A ternary search algorithm is a technique in computer science for finding the
7-
* minimum or maximum of a unimodal function The algorithm determines either
8-
* that the minimum or maximum cannot be in the first third of the domain or
9-
* that it cannot be in the last third of the domain, then repeats on the
10-
* remaining third.
6+
* A ternary search algorithm is a divide-and-conquer algorithm that searches for
7+
* a key in a sorted array by dividing the search domain into three parts.
118
*
129
* <p>
13-
* Worst-case performance Θ(log3(N)) Best-case performance O(1) Average
14-
* performance Θ(log3(N)) Worst-case space complexity O(1)
10+
* Worst-case performance: Θ(log3(N))<br>
11+
* Best-case performance: O(1)<br>
12+
* Average performance: Θ(log3(N))<br>
13+
* Worst-case space complexity: O(log3(N))
1514
*
1615
* @author Podshivalov Nikita (https://github.com/nikitap492)
1716
* @see SearchAlgorithm
1817
* @see IterativeBinarySearch
1918
*/
20-
public class TernarySearch implements SearchAlgorithm {
19+
public final class TernarySearch implements SearchAlgorithm {
2120

2221
/**
23-
* @param arr The **Sorted** array in which we will search the element.
22+
* Searches for a value in a sorted array using ternary search.
23+
*
24+
* @param arr The sorted array in which we will search for the element.
2425
* @param value The value that we want to search for.
25-
* @return The index of the element if found. Else returns -1.
26+
* @return The index of the element if found. Returns -1 if the array is null or empty, if value is null, or if not found.
2627
*/
2728
@Override
2829
public <T extends Comparable<T>> int find(T[] arr, T value) {
30+
if (arr == null || arr.length == 0 || value == null) {
31+
return -1;
32+
}
2933
return ternarySearch(arr, value, 0, arr.length - 1);
3034
}
3135

3236
/**
33-
* @param arr The **Sorted** array in which we will search the element.
37+
* Recursively searches for the specified key within the range [start, end].
38+
*
39+
* @param arr The sorted array in which we will search the element.
3440
* @param key The value that we want to search for.
35-
* @param start The starting index from which we will start Searching.
36-
* @param end The ending index till which we will Search.
37-
* @return Returns the index of the Element if found. Else returns -1.
41+
* @param start The starting index from which we will start searching.
42+
* @param end The ending index till which we will search.
43+
* @return Returns the index of the element if found. Else returns -1.
3844
*/
3945
private <T extends Comparable<T>> int ternarySearch(T[] arr, T key, int start, int end) {
4046
if (start > end) {
@@ -45,16 +51,22 @@ private <T extends Comparable<T>> int ternarySearch(T[] arr, T key, int start, i
4551
/* Second boundary: add 2/3 of length to start */
4652
int mid2 = start + 2 * (end - start) / 3;
4753

48-
if (key.compareTo(arr[mid1]) == 0) {
54+
int cmp1 = key.compareTo(arr[mid1]);
55+
if (cmp1 == 0) {
4956
return mid1;
50-
} else if (key.compareTo(arr[mid2]) == 0) {
57+
}
58+
59+
int cmp2 = key.compareTo(arr[mid2]);
60+
if (cmp2 == 0) {
5161
return mid2;
52-
} /* Search the first (1/3) rd part of the array.*/ else if (key.compareTo(arr[mid1]) < 0) {
53-
return ternarySearch(arr, key, start, --mid1);
54-
} /* Search 3rd (1/3)rd part of the array */ else if (key.compareTo(arr[mid2]) > 0) {
55-
return ternarySearch(arr, key, ++mid2, end);
56-
} /* Search middle (1/3)rd part of the array */ else {
57-
return ternarySearch(arr, key, mid1, mid2);
62+
}
63+
64+
if (cmp1 < 0) {
65+
return ternarySearch(arr, key, start, mid1 - 1);
66+
} else if (cmp2 > 0) {
67+
return ternarySearch(arr, key, mid2 + 1, end);
68+
} else {
69+
return ternarySearch(arr, key, mid1 + 1, mid2 - 1);
5870
}
5971
}
6072
}

‎src/test/java/com/thealgorithms/searches/TernarySearchTest.java‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.thealgorithms.searches;
22

33
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
45

56
import org.junit.jupiter.api.Test;
67

@@ -78,4 +79,65 @@ void testEdgeCaseEmptyArray() {
7879

7980
assertEquals(-1, index, "Should return -1 for an empty array");
8081
}
82+
83+
@Test
84+
void testNullArrayReturnsMinusOne() {
85+
TernarySearch search = new TernarySearch();
86+
assertEquals(-1, search.find(null, 5));
87+
}
88+
89+
@Test
90+
void testNullKeyReturnsMinusOne() {
91+
Integer[] arr = {1, 2, 3, 4, 5};
92+
TernarySearch search = new TernarySearch();
93+
assertEquals(-1, search.find(arr, null));
94+
}
95+
96+
@Test
97+
void testSingleElementPresent() {
98+
Integer[] arr = {42};
99+
TernarySearch search = new TernarySearch();
100+
assertEquals(0, search.find(arr, 42));
101+
}
102+
103+
@Test
104+
void testSingleElementAbsent() {
105+
Integer[] arr = {42};
106+
TernarySearch search = new TernarySearch();
107+
assertEquals(-1, search.find(arr, 24));
108+
}
109+
110+
@Test
111+
void testTwoElements() {
112+
Integer[] arr = {10, 20};
113+
TernarySearch search = new TernarySearch();
114+
assertEquals(0, search.find(arr, 10));
115+
assertEquals(1, search.find(arr, 20));
116+
assertEquals(-1, search.find(arr, 15));
117+
}
118+
119+
@Test
120+
void testMiddlePartitionSearch() {
121+
Integer[] arr = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19};
122+
TernarySearch search = new TernarySearch();
123+
assertEquals(3, search.find(arr, 7));
124+
assertEquals(4, search.find(arr, 9));
125+
assertEquals(5, search.find(arr, 11));
126+
}
127+
128+
@Test
129+
void testDuplicateElements() {
130+
Integer[] arr = {1, 2, 2, 2, 3};
131+
TernarySearch search = new TernarySearch();
132+
int index = search.find(arr, 2);
133+
assertTrue(index >= 1 && index <= 3, "Index should be one of the occurrences of 2");
134+
}
135+
136+
@Test
137+
void testGenericStrings() {
138+
String[] words = {"apple", "banana", "cherry", "date", "elderberry"};
139+
TernarySearch search = new TernarySearch();
140+
assertEquals(2, search.find(words, "cherry"));
141+
assertEquals(-1, search.find(words, "fig"));
142+
}
81143
}

0 commit comments

Comments
 (0)