Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.williamfiset.algorithms.dp;

/**
* Finds the longest increasing subsequence within an array of numbers obtained by removing the
* fewest possible elements from the initial array. Complexity: O(n * log n)
*/
public class LongestIncreasingSubsequenceFast {
/** Main function. */
public static void main(String[] args) {

System.out.println(longestIncreasingSubsequenceLength(new int[] {1, 3, 2, 4, 3})); // 3
System.out.println(longestIncreasingSubsequenceLength(new int[] {2, 7, 4, 3, 8})); // 3
System.out.println(longestIncreasingSubsequenceLength(new int[] {5, 4, 3, 2, 1})); // 1
System.out.println(
longestIncreasingSubsequenceLength(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9})); // 9
System.out.println(
longestIncreasingSubsequenceLength(
new int[] {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15})); // 6
}

/**
* Function performing the binary search step of the main algorithm.
*
* @param ar parsed array
* @param l lower bound representing the leftmost index of the array
* @param r upper bound representing the rightmost index of the array
* @param key value to be inserted
* @return index before which the key can be inserted
*/
static int binarySearch(int[] ar, int l, int r, int key) {
while (r - l > 1) {
int m = l + (r - l) / 2;
if (ar[m] >= key) {
r = m;
} else {
l = m;
}
}
return r;
}

/**
* Function computing the length of the longest increasing subsequence from an array.
*
* @param ar initial array
* @return the length of the longest increasing subsequence
*/
static int longestIncreasingSubsequenceLength(int[] ar) {
int size = ar.length;
if (size == 0) {
return 0;
}
int[] lis = new int[size];
int len;
lis[0] = ar[0];
len = 1;
for (int i = 1; i < size; i++) {
// minimum value needs updating
if (ar[i] < lis[0]) {
lis[0] = ar[i];
} else if (ar[i] > lis[len - 1]) { // new value can be safely appended
lis[len++] = ar[i];
} else { // position needs to be found for new value
lis[binarySearch(lis, -1, len - 1, ar[i])] = ar[i];
}
}
return len;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.williamfiset.algorithms.dp;

import org.junit.Assert;
import org.junit.Test;

/**
* Checking that the returned values of the two different implementations are equal (consistent).
*/
public class LongestIncreasingSubsequenceFastTest {
@Test
public void longestIncreasingSubsequenceFastEmpty() {
int[] array = {};
Assert.assertEquals(
0, LongestIncreasingSubsequenceFast.longestIncreasingSubsequenceLength(array));
Assert.assertEquals(
LongestIncreasingSubsequence.lis(array),
LongestIncreasingSubsequenceFast.longestIncreasingSubsequenceLength(array));
}

@Test
public void longestIncreasingSubsequenceFastSingle() {
int[] array = {3};
Assert.assertEquals(
1, LongestIncreasingSubsequenceFast.longestIncreasingSubsequenceLength(array));
Assert.assertEquals(
LongestIncreasingSubsequence.lis(array),
LongestIncreasingSubsequenceFast.longestIncreasingSubsequenceLength(array));
}

@Test
public void longestIncreasingSubsequenceFastRandom() {
int[][] array = {
{1, 3, 2, 4, 3},
{2, 7, 4, 3, 8},
{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}
};

Assert.assertEquals(
3, LongestIncreasingSubsequenceFast.longestIncreasingSubsequenceLength(array[0]));
Assert.assertEquals(
3, LongestIncreasingSubsequenceFast.longestIncreasingSubsequenceLength(array[1]));
Assert.assertEquals(
6, LongestIncreasingSubsequenceFast.longestIncreasingSubsequenceLength(array[2]));

for (int[] ints : array) {
Assert.assertEquals(
LongestIncreasingSubsequence.lis(ints),
LongestIncreasingSubsequenceFast.longestIncreasingSubsequenceLength(ints));
}
}

@Test
public void longestIncreasingSubsequenceFastAscending() {
int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9};
Assert.assertEquals(
9, LongestIncreasingSubsequenceFast.longestIncreasingSubsequenceLength(array));
Assert.assertEquals(
LongestIncreasingSubsequence.lis(array),
LongestIncreasingSubsequenceFast.longestIncreasingSubsequenceLength(array));
}

@Test
public void longestIncreasingSubsequenceFastDescending() {
int[] array = {5, 4, 3, 2, 1};
Assert.assertEquals(
1, LongestIncreasingSubsequenceFast.longestIncreasingSubsequenceLength(array));
Assert.assertEquals(
LongestIncreasingSubsequence.lis(array),
LongestIncreasingSubsequenceFast.longestIncreasingSubsequenceLength(array));
}

@Test(expected = NullPointerException.class)
public void longestIncreasingSubsequenceFastNull() {
LongestIncreasingSubsequenceFast.longestIncreasingSubsequenceLength(null);
LongestIncreasingSubsequence.lis(null);
}
}