Skip to content

Commit 9f5cb98

Browse files
Peak Element - Leetcode 162
1 parent 476fc41 commit 9f5cb98

File tree

3 files changed

+190
-0
lines changed

3 files changed

+190
-0
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package array.peak_element;
2+
3+
/**
4+
* Implements a solution to find a peak element using binary search.
5+
*/
6+
public class BinarySearch {
7+
8+
/**
9+
* Finds a peak element in an array using a modified binary search algorithm.
10+
*
11+
* <p>The O(log n) time complexity requirement is met by applying binary search.
12+
* The algorithm works by comparing the middle element {@code nums[mid]} with
13+
* its right neighbor {@code nums[mid + 1]}.
14+
* <ul>
15+
* <li>If {@code nums[mid] < nums[mid + 1]}, it implies that the array is
16+
* "climbing" upwards. A peak must exist on the right side, so we
17+
* discard the left half by setting {@code low = mid + 1}.</li>
18+
* <li>If {@code nums[mid] >= nums[mid + 1]}, it implies that {@code nums[mid]}
19+
* is either a peak itself or is on a "downward" slope. In this case,
20+
* a peak is guaranteed to be in the left half (including {@code mid}),
21+
* so we discard the right half by setting {@code high = mid}.</li>
22+
* </ul>
23+
* The loop continues until {@code low} and {@code high} converge, at which
24+
* point they identify the index of a peak element. This approach elegantly
25+
* handles all cases, including peaks at the array boundaries, due to the
26+
* problem's assumption that elements outside the array are negative infinity.
27+
*
28+
* @param nums The 0-indexed integer array to search.
29+
* @return The index of any peak element.
30+
*/
31+
public int peakElement(int[] nums) {
32+
if (nums == null || nums.length == 0) {
33+
// Returning -1 or throwing an exception are valid strategies.
34+
throw new IllegalArgumentException("Input array cannot be null or empty.");
35+
}
36+
37+
int low = 0;
38+
int high = nums.length - 1;
39+
40+
// Binary search until the search space is reduced to a single element.
41+
while (low < high) {
42+
// Prevent potential integer overflow
43+
int mid = low + (high - low) / 2;
44+
45+
// If the middle element is less than its right neighbor, a peak
46+
// must be in the right half of the array.
47+
if (nums[mid] < nums[mid + 1]) {
48+
low = mid + 1;
49+
} else {
50+
// Otherwise, the peak is in the left half, including mid itself.
51+
high = mid;
52+
}
53+
}
54+
55+
// When the loop terminates, low == high, pointing to a peak element.
56+
return low;
57+
}
58+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Provides solutions for finding a peak element in an array.
3+
*
4+
* <h2>Problem: Find Peak Element</h2>
5+
*
6+
* <p>A peak element is an element that is strictly greater than its neighbors.
7+
* Given a 0-indexed integer array {@code nums}, the goal is to find a peak
8+
* element and return its index. If the array contains multiple peaks, returning
9+
* the index to any of the peaks is acceptable.
10+
*
11+
* <p>A key condition is that we can imagine {@code nums[-1] = nums[n] = -∞}.
12+
* This means an element at the edge of the array only needs to be greater than
13+
* its single adjacent neighbor to be considered a peak. This condition also
14+
* guarantees that a peak element always exists.
15+
*
16+
* <p>The solution must run in O(log n) time complexity.
17+
*
18+
* <p></p>For more details, see the problem on LeetCode:
19+
* <a href="https://leetcode.com/problems/find-peak-element/">162. Find Peak Element</a>
20+
*/
21+
package array.peak_element;
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package array.peak_element;
2+
3+
import org.junit.jupiter.api.BeforeEach;
4+
import org.junit.jupiter.api.DisplayName;
5+
import org.junit.jupiter.api.Test;
6+
7+
import java.util.Set;
8+
9+
import static org.junit.jupiter.api.Assertions.*;
10+
11+
/**
12+
* Test suite for the BinarySearch class to find a peak element.
13+
*/
14+
class BinarySearchTest {
15+
16+
private BinarySearch binarySearch;
17+
18+
@BeforeEach
19+
void setUp() {
20+
binarySearch = new BinarySearch();
21+
}
22+
23+
@Test
24+
@DisplayName("Test with a simple peak in the middle of the array")
25+
void testPeakElement_simplePeak() {
26+
int[] nums = {1, 2, 3, 1};
27+
assertEquals(2, binarySearch.peakElement(nums), "Should find the peak at index 2");
28+
}
29+
30+
@Test
31+
@DisplayName("Test with a peak at the beginning of the array")
32+
void testPeakElement_peakAtStart() {
33+
int[] nums = {5, 4, 3, 2, 1};
34+
assertEquals(0, binarySearch.peakElement(nums), "Should find the peak at the start");
35+
}
36+
37+
@Test
38+
@DisplayName("Test with a peak at the end of the array")
39+
void testPeakElement_peakAtEnd() {
40+
int[] nums = {1, 2, 3, 4, 5};
41+
assertEquals(4, binarySearch.peakElement(nums), "Should find the peak at the end");
42+
}
43+
44+
@Test
45+
@DisplayName("Test with multiple peaks, should return any valid peak index")
46+
void testPeakElement_multiplePeaks() {
47+
int[] nums = {1, 2, 1, 3, 5, 6, 4};
48+
// Valid peaks are at index 1 (value 2) and index 5 (value 6)
49+
Set<Integer> validPeakIndices = Set.of(1, 5);
50+
int result = binarySearch.peakElement(nums);
51+
assertTrue(validPeakIndices.contains(result), "Result should be one of the valid peak indices");
52+
}
53+
54+
@Test
55+
@DisplayName("Test with a single-element array")
56+
void testPeakElement_singleElement() {
57+
int[] nums = {42};
58+
assertEquals(0, binarySearch.peakElement(nums), "A single element is always a peak");
59+
}
60+
61+
@Test
62+
@DisplayName("Test with a two-element ascending array")
63+
void testPeakElement_twoElementsAscending() {
64+
int[] nums = {10, 20};
65+
assertEquals(1, binarySearch.peakElement(nums), "Peak should be the larger element at index 1");
66+
}
67+
68+
@Test
69+
@DisplayName("Test with a two-element descending array")
70+
void testPeakElement_twoElementsDescending() {
71+
int[] nums = {20, 10};
72+
assertEquals(0, binarySearch.peakElement(nums), "Peak should be the larger element at index 0");
73+
}
74+
75+
@Test
76+
@DisplayName("Test with an array containing a plateau")
77+
void testPeakElement_withPlateau() {
78+
int[] nums = {1, 5, 5, 2};
79+
// The algorithm should find index 1 as a peak because nums[1] >= nums[2]
80+
assertEquals(1, binarySearch.peakElement(nums), "Should handle plateaus correctly");
81+
}
82+
83+
@Test
84+
@DisplayName("Test with a zig-zag array")
85+
void testPeakElement_zigZag() {
86+
int[] nums = {1, 5, 1, 5, 1, 5, 1};
87+
Set<Integer> validPeakIndices = Set.of(1, 3, 5);
88+
int result = binarySearch.peakElement(nums);
89+
assertTrue(validPeakIndices.contains(result), "Result should be a valid peak in the zig-zag array");
90+
}
91+
92+
@Test
93+
@DisplayName("Test with a long, strictly increasing then decreasing array")
94+
void testPeakElement_longArray() {
95+
int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1};
96+
assertEquals(7, binarySearch.peakElement(nums), "Should find the peak in a long array");
97+
}
98+
99+
@Test
100+
@DisplayName("Test with null input should throw IllegalArgumentException")
101+
void testPeakElement_nullInput() {
102+
assertThrows(IllegalArgumentException.class, () -> binarySearch.peakElement(null), "Should throw IllegalArgumentException for null input");
103+
}
104+
105+
@Test
106+
@DisplayName("Test with empty input should throw IllegalArgumentException")
107+
void testPeakElement_emptyInput() {
108+
int[] nums = {};
109+
assertThrows(IllegalArgumentException.class, () -> binarySearch.peakElement(nums), "Should throw IllegalArgumentException for an empty array");
110+
}
111+
}

0 commit comments

Comments
 (0)