Skip to content

Commit ad5e496

Browse files
Add LongestIncreasingSubsequenceNLogN (#6221)
1 parent c8177e3 commit ad5e496

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
/**
4+
* Implementation of the Longest Increasing Subsequence (LIS) problem using
5+
* an O(n log n) dynamic programming solution enhanced with binary search.
6+
*
7+
* @author Vusal Huseynov (https://github.com/huseynovvusal)
8+
*/
9+
public final class LongestIncreasingSubsequenceNLogN {
10+
private LongestIncreasingSubsequenceNLogN() {
11+
}
12+
13+
/**
14+
* Finds the index of the smallest element in the array that is greater than
15+
* or equal to the target using binary search. The search is restricted to
16+
* the first `size` elements of the array.
17+
*
18+
* @param arr The array to search in (assumed to be sorted up to `size`).
19+
* @param size The number of valid elements in the array.
20+
* @param target The target value to find the lower bound for.
21+
* @return The index of the lower bound.
22+
*/
23+
private static int lowerBound(int[] arr, int target, int size) {
24+
int l = 0;
25+
int r = size;
26+
27+
while (l < r) {
28+
int mid = l + (r - l) / 2;
29+
30+
if (target > arr[mid]) {
31+
// Move right if target is greater than mid element
32+
l = mid + 1;
33+
} else {
34+
// Move left if target is less than or equal to mid element
35+
r = mid;
36+
}
37+
}
38+
39+
// Return the index where the target can be inserted
40+
return l;
41+
}
42+
43+
/**
44+
* Calculates the length of the Longest Increasing Subsequence (LIS) in the given array.
45+
*
46+
* @param arr The input array of integers.
47+
* @return The length of the LIS.
48+
*/
49+
public static int lengthOfLIS(int[] arr) {
50+
if (arr == null || arr.length == 0) {
51+
return 0; // Return 0 for empty or null arrays
52+
}
53+
54+
// tails[i] - the smallest end element of an increasing subsequence of length i+1
55+
int[] tails = new int[arr.length];
56+
// size - the length of the longest increasing subsequence found so far
57+
int size = 0;
58+
59+
for (int x : arr) {
60+
// Find the position to replace or extend the subsequence
61+
int index = lowerBound(tails, x, size);
62+
63+
// Update the tails array with the current element
64+
tails[index] = x;
65+
66+
// If the element extends the subsequence, increase the size
67+
if (index == size) {
68+
size++;
69+
}
70+
}
71+
72+
// Return the length of the LIS
73+
return size;
74+
}
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import java.util.stream.Stream;
6+
import org.junit.jupiter.params.ParameterizedTest;
7+
import org.junit.jupiter.params.provider.Arguments;
8+
import org.junit.jupiter.params.provider.MethodSource;
9+
10+
public class LongestIncreasingSubsequenceNLogNTest {
11+
12+
private static Stream<Arguments> provideTestCases() {
13+
return Stream.of(Arguments.of(new int[] {10, 9, 2, 5, 3, 7, 101, 18}, 4), Arguments.of(new int[] {0, 1, 0, 3, 2, 3}, 4), Arguments.of(new int[] {7, 7, 7, 7, 7}, 1), Arguments.of(new int[] {1, 3, 5, 4, 7}, 4), Arguments.of(new int[] {}, 0), Arguments.of(new int[] {10}, 1),
14+
Arguments.of(new int[] {3, 10, 2, 1, 20}, 3), Arguments.of(new int[] {50, 3, 10, 7, 40, 80}, 4));
15+
}
16+
17+
@ParameterizedTest
18+
@MethodSource("provideTestCases")
19+
public void testLengthOfLIS(int[] input, int expected) {
20+
assertEquals(expected, LongestIncreasingSubsequenceNLogN.lengthOfLIS(input));
21+
}
22+
}

0 commit comments

Comments
 (0)