Skip to content

Commit ed2928f

Browse files
authored
fix: avoid out-of-bounds access in HowManyTimesRotated on small/unrotated arrays (#7626)
The old mid-1/mid+1 comparison indexed out of bounds for a 1-2 element array and never terminated correctly for an unrotated array. Replaced with a standard binary search for the rotation pivot.
1 parent b33b75b commit ed2928f

2 files changed

Lines changed: 31 additions & 16 deletions

File tree

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

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@
1616
The position of the minimum element will give the number of times the array has been rotated
1717
from its initial sorted position.
1818
Eg. For [2,5,6,8,11,12,15,18], 1 rotation gives [5,6,8,11,12,15,18,2], 2 rotations
19-
[6,8,11,12,15,18,2,5] and so on. Finding the minimum element will take O(N) time but, we can use
20-
Binary Search to find the minimum element, we can reduce the complexity to O(log N). If we look
21-
at the rotated array, to identify the minimum element (say a[i]), we observe that
22-
a[i-1]>a[i]<a[i+1].
19+
[6,8,11,12,15,18,2,5] and so on. Finding the minimum element will take O(N) time but, we can use
20+
Binary Search to reduce the complexity to O(log N): at each step compare a[mid] with a[high].
21+
If a[mid] > a[high], the minimum lies to the right, so low = mid + 1; otherwise it lies at mid
22+
or to the left, so high = mid. This converges to the minimum's index without ever reading
23+
a[mid-1] or a[mid+1], so it also works on arrays of size 0-2 and unrotated arrays.
2324
2425
Some other test cases:
2526
1. [1,2,3,4] Number of rotations: 0 or 4(Both valid)
26-
2. [15,17,2,3,5] Number of rotations: 3
27+
2. [15,17,2,3,5] Number of rotations: 2
2728
*/
2829
final class HowManyTimesRotated {
2930
private HowManyTimesRotated() {
@@ -44,20 +45,16 @@ public static void main(String[] args) {
4445
public static int rotated(int[] a) {
4546
int low = 0;
4647
int high = a.length - 1;
47-
int mid = 0; // low + (high-low)/2 = (low + high)/2
4848

49-
while (low <= high) {
50-
mid = low + (high - low) / 2;
51-
52-
if (a[mid] < a[mid - 1] && a[mid] < a[mid + 1]) {
53-
break;
54-
} else if (a[mid] > a[mid - 1] && a[mid] < a[mid + 1]) {
55-
high = mid + 1;
56-
} else if (a[mid] > a[mid - 1] && a[mid] > a[mid + 1]) {
57-
low = mid - 1;
49+
while (low < high) {
50+
int mid = low + (high - low) / 2;
51+
if (a[mid] > a[high]) {
52+
low = mid + 1;
53+
} else {
54+
high = mid;
5855
}
5956
}
6057

61-
return mid;
58+
return low;
6259
}
6360
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import static org.junit.jupiter.api.Assertions.assertEquals;
44

5+
import java.util.concurrent.TimeUnit;
56
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.api.Timeout;
68

79
public class HowManyTimesRotatedTest {
810

@@ -13,4 +15,20 @@ public void testHowManyTimesRotated() {
1315
int[] arr2 = {15, 17, 2, 3, 5};
1416
assertEquals(2, HowManyTimesRotated.rotated(arr2));
1517
}
18+
19+
/** An unrotated (already sorted) array should resolve to 0 rotations without hanging. */
20+
@Test
21+
@Timeout(value = 5, unit = TimeUnit.SECONDS, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
22+
public void testHowManyTimesRotatedOnUnrotatedArray() {
23+
int[] arr = {2, 5, 6, 8, 11, 12, 15, 18};
24+
assertEquals(0, HowManyTimesRotated.rotated(arr));
25+
}
26+
27+
/** Arrays of size 1 and 2 should not throw ArrayIndexOutOfBoundsException. */
28+
@Test
29+
public void testHowManyTimesRotatedOnSmallArrays() {
30+
assertEquals(0, HowManyTimesRotated.rotated(new int[] {5}));
31+
assertEquals(0, HowManyTimesRotated.rotated(new int[] {1, 2}));
32+
assertEquals(1, HowManyTimesRotated.rotated(new int[] {2, 1}));
33+
}
1634
}

0 commit comments

Comments
 (0)