Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit dd0fb31

Browse files
Merge pull request #2 from borrelunde/median_of_two_sorted_arrays
LeetCode problem: 4. Median of Two Sorted Arrays
2 parents d591cf6 + 5b2f692 commit dd0fb31

File tree

3 files changed

+341
-0
lines changed

3 files changed

+341
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# 4. Median of Two Sorted Arrays
2+
3+
Difficulty: `Hard`
4+
Topics: `Array`, `Binary Search`, `Divide and Conquer`
5+
6+
Given two sorted arrays `nums1` and `nums2` of size `m` and `n` respectively, return **the median** of the two sorted arrays.
7+
8+
The overall run time complexity should be `O(log (m+n))`.
9+
10+
**Example 1:**
11+
12+
```text
13+
Input: nums1 = [1,3], nums2 = [2]
14+
Output: 2.00000
15+
Explanation: merged array = [1,2,3] and median is 2.
16+
```
17+
18+
```text
19+
Input: nums1 = [1,2], nums2 = [3,4]
20+
Output: 2.50000
21+
Explanation: merged array = [1,2,3,4] and median is (2 + 3) / 2 = 2.5.
22+
```
23+
24+
**Constraints:**
25+
26+
- `nums1.length == m`
27+
- `nums2.length == n`
28+
- `0 <= m <= 1000`
29+
- `0 <= n <= 1000`
30+
- `1 <= m + n <= 2000`
31+
- `-10^6 <= nums1[i], nums2[i] <= 10^6`
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package com.bl.median_of_two_sorted_arrays;
2+
3+
/**
4+
* This is the solution to the LeetCode problem: 4. Median of Two Sorted Arrays
5+
*
6+
* @author Børre A. Opedal Lunde
7+
* @since 2024年01月25日
8+
*/
9+
@SuppressWarnings("ManualMinMaxCalculation")
10+
public class Solution {
11+
12+
// This solution is made with readability in mind. It is not the most
13+
// efficient solution, but it should be understandable. And that's what
14+
// matters more for me.
15+
//
16+
// There are many ways to optimise for performance and memory usage. For
17+
// example by using bitwise operations for division, inlining methods and
18+
// constants, using the original arrays instead of copying them, and
19+
// reducing the number of helper variables and methods, etc.
20+
21+
private static final int HYPOTHETICAL_NEGATIVE_INFINITY = 0x80000000; // Integer minimum value.
22+
private static final int HYPOTHETICAL_POSITIVE_INFINITY = 0x7fffffff; // Integer maximum value.
23+
24+
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
25+
26+
// Determine the smallest and largest array.
27+
final int[] leftArray = getSmallestArrayOf(nums1, nums2);
28+
final int[] rightArray = getLargestArrayOf(nums1, nums2);
29+
30+
// Calculate the sizes of the arrays and their combined length. This
31+
// will be used in the partitioning of the arrays.
32+
final int leftArraySize = leftArray.length;
33+
final int rightArraySize = rightArray.length;
34+
final int combinedArraysLength = leftArraySize + rightArraySize;
35+
36+
// According to the problem description, the arrays should never be
37+
// empty. If they are, then we throw an exception.
38+
if (leftArraySize == 0 && rightArraySize == 0) {
39+
throw new IllegalArgumentException("The arrays are empty.");
40+
}
41+
42+
// Low and high for the binary search.
43+
int low = 0;
44+
int high = leftArraySize;
45+
46+
// Binary search.
47+
while (low <= high) {
48+
49+
// In simple steps, this is what goes on:
50+
//
51+
// 1. Calculate the partition indices for the arrays.
52+
//
53+
// 2. Get the left and right values of the partitions.
54+
//
55+
// 3. Check if the left side of the partition is less than or equal
56+
// to the right side of the partition.
57+
//
58+
// 4. If the left side is less than or equal to the right side,
59+
// then we have found the median.
60+
//
61+
// 5. If the left side is greater than the right side, then we need
62+
// to move the partition to the left.
63+
//
64+
// 6. If the left side is less than the right side, then we need to
65+
// move the partition to the right.
66+
67+
// Calculate the partition indices for the arrays.
68+
final int leftArrayPartitionIndex = (low + high) / 2;
69+
final int rightArrayPartitionIndex = (combinedArraysLength + 1) / 2 - leftArrayPartitionIndex;
70+
71+
// Get values at partition indices.
72+
final int leftArrayLeftValue = getLeftValueOfPartition(leftArray, leftArrayPartitionIndex);
73+
final int leftArrayRightValue = getRightValueOfPartition(leftArray, leftArrayPartitionIndex);
74+
75+
final int rightArrayLeftValue = getLeftValueOfPartition(rightArray, rightArrayPartitionIndex);
76+
final int rightArrayRightValue = getRightValueOfPartition(rightArray, rightArrayPartitionIndex);
77+
78+
// The left side is OK if the left value is less than or equal to
79+
// the right value. Equally, the right side is OK if the right
80+
// value is less than or equal to the left value.
81+
final boolean leftSideIsOk = leftArrayLeftValue <= rightArrayRightValue;
82+
final boolean rightSideIsOk = rightArrayLeftValue <= leftArrayRightValue;
83+
84+
// If both sides are OK, then we can calculate the median.
85+
if (leftSideIsOk && rightSideIsOk) {
86+
87+
// Get the maximum of the left values and the minimum of the
88+
// right values. The maximum and minimum are the closest values
89+
// to the middle.
90+
final int leftValue = getMaximumOf(leftArrayLeftValue, rightArrayLeftValue);
91+
final int rightValue = getMinimumOf(leftArrayRightValue, rightArrayRightValue);
92+
93+
final double median;
94+
if (isEven(combinedArraysLength)) {
95+
// When the combined length is even, we need to calculate
96+
// the average of the two middle values.
97+
median = (leftValue + rightValue) / 2.0;
98+
} else {
99+
// It is easier when the combined length is odd. Then the
100+
// median is the middle (left) value.
101+
median = leftValue;
102+
}
103+
104+
System.out.printf("Returning median: %f%n", median);
105+
return median;
106+
}
107+
108+
// Move the partition to the left if the left side is not OK.
109+
else if (! leftSideIsOk) {
110+
high = leftArrayPartitionIndex - 1;
111+
}
112+
113+
// Vice versa for the right side.
114+
else {
115+
low = leftArrayPartitionIndex + 1;
116+
}
117+
}
118+
119+
// Given the constraints of the problem, we should never reach this
120+
// point. If we reach here regardless, then something is wrong.
121+
throw new UnsupportedOperationException("Something went wrong.");
122+
}
123+
124+
private static int getLeftValueOfPartition(final int[] array, final int index) {
125+
if (index <= 0) {
126+
// There is no left value at this index, so we return the smallest
127+
// possible value. We can then be sure that the value to its right
128+
// is greater or equal to it.
129+
return HYPOTHETICAL_NEGATIVE_INFINITY;
130+
}
131+
return array[index - 1];
132+
}
133+
134+
private static int getRightValueOfPartition(final int[] array, final int index) {
135+
if (index >= array.length) {
136+
// The same goes for the right side, just the other way around.
137+
return HYPOTHETICAL_POSITIVE_INFINITY;
138+
}
139+
return array[index];
140+
}
141+
142+
private static int[] getSmallestArrayOf(int[] a, int[] b) {
143+
return a.length <= b.length ? a : b;
144+
}
145+
146+
private static int[] getLargestArrayOf(int[] a, int[] b) {
147+
return a.length <= b.length ? b : a;
148+
}
149+
150+
private static boolean isEven(final int number) {
151+
return number % 2 == 0;
152+
}
153+
154+
private static int getMaximumOf(final int a, final int b) {
155+
return a >= b ? a : b;
156+
}
157+
158+
private static int getMinimumOf(final int a, final int b) {
159+
return a >= b ? b : a;
160+
}
161+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package com.bl.median_of_two_sorted_arrays;
2+
3+
import org.junit.jupiter.api.DisplayName;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static org.junit.jupiter.api.Assertions.assertEquals;
7+
import static org.junit.jupiter.api.Assertions.assertThrows;
8+
9+
/**
10+
* This is the test to the LeetCode problem: 4. Median of Two Sorted Arrays
11+
*
12+
* @author Børre A. Opedal Lunde
13+
* @since 2024年01月25日
14+
*/
15+
@DisplayName("Median of Two Sorted Arrays")
16+
class SolutionTest {
17+
18+
private final Solution solution = new Solution();
19+
20+
@Test
21+
@DisplayName("Example one")
22+
void exampleOne() {
23+
int[] nums1 = {1, 3};
24+
int[] nums2 = {2};
25+
26+
double expected = 2.00000;
27+
double actual = solution.findMedianSortedArrays(nums1, nums2);
28+
29+
assertEquals(expected, actual);
30+
}
31+
32+
@Test
33+
@DisplayName("Example two")
34+
void exampleTwo() {
35+
int[] nums1 = {1, 2};
36+
int[] nums2 = {3, 4};
37+
38+
double expected = 2.50000;
39+
double actual = solution.findMedianSortedArrays(nums1, nums2);
40+
41+
assertEquals(expected, actual);
42+
}
43+
44+
@Test
45+
@DisplayName("Even combined arrays length")
46+
void evenCombinedArraysLength() {
47+
int[] nums1 = {1, 3};
48+
int[] nums2 = {2, 4};
49+
50+
double expected = 2.5;
51+
double actual = solution.findMedianSortedArrays(nums1, nums2);
52+
53+
assertEquals(expected, actual);
54+
}
55+
56+
@Test
57+
@DisplayName("Odd combined arrays length")
58+
void oddCombinedArraysLength() {
59+
int[] nums1 = {1, 3};
60+
int[] nums2 = {2};
61+
62+
double expected = 2.0;
63+
double actual = solution.findMedianSortedArrays(nums1, nums2);
64+
65+
assertEquals(expected, actual);
66+
}
67+
68+
@Test
69+
@DisplayName("One empty array")
70+
void oneEmptyArray() {
71+
int[] nums1 = {};
72+
int[] nums2 = {1, 2, 3, 4, 5};
73+
74+
double expected = 3.0;
75+
double actual = solution.findMedianSortedArrays(nums1, nums2);
76+
77+
assertEquals(expected, actual);
78+
}
79+
80+
@Test
81+
@DisplayName("Both arrays empty")
82+
void bothArraysEmpty() {
83+
int[] nums1 = {};
84+
int[] nums2 = {};
85+
86+
assertThrows(IllegalArgumentException.class,
87+
() -> solution.findMedianSortedArrays(nums1, nums2));
88+
}
89+
90+
@Test
91+
@DisplayName("Arrays with negative numbers")
92+
void arraysWithNegativeNumbers() {
93+
int[] nums1 = {- 5, - 3, - 1};
94+
int[] nums2 = {- 2, - 1, 4, 6};
95+
96+
double expected = - 1.0;
97+
double actual = solution.findMedianSortedArrays(nums1, nums2);
98+
99+
assertEquals(expected, actual);
100+
}
101+
102+
@Test
103+
@DisplayName("First array large and second array small")
104+
void firstArrayLargeAndSecondArraySmall() {
105+
int[] nums1 = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
106+
int[] nums2 = {1, 2, 3};
107+
108+
double expected = 8.0;
109+
double actual = solution.findMedianSortedArrays(nums1, nums2);
110+
111+
assertEquals(expected, actual);
112+
}
113+
114+
@Test
115+
@DisplayName("First array small and second array large")
116+
void firstArraySmallAndSecondArrayLarge() {
117+
int[] nums1 = {1, 2, 3};
118+
int[] nums2 = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
119+
120+
double expected = 8.0;
121+
double actual = solution.findMedianSortedArrays(nums1, nums2);
122+
123+
assertEquals(expected, actual);
124+
}
125+
126+
@Test
127+
@DisplayName("Arrays with a single element each")
128+
void arraysWithASingleElementEach() {
129+
int[] nums1 = {1};
130+
int[] nums2 = {2};
131+
132+
double expected = 1.5;
133+
double actual = solution.findMedianSortedArrays(nums1, nums2);
134+
135+
assertEquals(expected, actual);
136+
}
137+
138+
@Test
139+
@DisplayName("Arrays with identical elements")
140+
void arraysWithIdenticalElements() {
141+
int[] nums1 = {2, 2, 2};
142+
int[] nums2 = {2, 2, 2};
143+
144+
double expected = 2.0;
145+
double actual = solution.findMedianSortedArrays(nums1, nums2);
146+
147+
assertEquals(expected, actual);
148+
}
149+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /