I have this assignment where I'm supposed to code an algorithm for finding the quantity of Longest Increasing Subsequences from an array of (different) integers and print them all.
I developed one which always gives me the right answer, but I have 2 issues with it:
It's a little slow
When the given array is between 150-200 integers long, it crashes with:
OutOfMemory: heap space
I understand why it's a little slow, and to tell you the truth, it's not that slow when you think of Brute Force Algorithm alternatives.
And also, I'd like to be able to return the quantity of LISs independently from searching them (like finding the number of cheapest paths in a graph without returning them all), but I just can't seem to find a solution without counting them after finding them.
A little explanation:
The algorithm creates an (n+1)*(n+1) matrix (n = length of given array), rows representing given array, columns representing given array after sorting (ascending), fills the first row and the first column with 0s and then loops over it using different conditions to either:
- add an integer to the array(s) existing at given slot in this matrix (mat[i, j])
- copies all the existing arrays of the slot above and the slot aside (mat[i-1, j], mat[i, j-1]) into the current slot (without duplicates)
- copies the array(s) from the slot (above or aside) in which they are longer into current slot)
- just fills it with "nothing"
n.b.: I'm not actually creating a (n+1)*(n+1) matrix. Instead I create a 2*(n+1) matrix in which I only keep the last row calculated and the one I'm about to, then switch.
My problem might also come from my testing. I just can't seem to see it.
import java.util.Arrays;
import java.util.ArrayList;
public class LIS {
private int[] arr;
public LIS(int[] arr) {
this.arr = new int[arr.length];
System.arraycopy(arr, 0, this.arr, 0, arr.length);
}
public static int BSBinArray(int[] arr, int end, int value) {...}// BinarySearch
public static void mergeSort(int [] arr, int low, int high){...}// 1st part of MergeSort
public static int[] mergeSort(int [] arr){...}// 2nd part of MergeSort
public int LISLength() {// get exclusively the length of the LIS(s)
int[] help = new int[arr.length];
help[0] = arr[0];
int end = 0;
for (int i = 0; i < arr.length; i++) {
int index = BSBinArray(help, end, arr[i]);
help[index] = arr[i];
if(index > end) end++;
}
return end+1;
}
// returns a Node (class below) with all the info in it
private Node buildMatrix() {
int maxLength = LISLength();
int[] sortArr = mergeSort(arr);
Node[][] mat = new Node[2][arr.length + 1];
for (int i = 0; i < mat.length; i++) {
mat[i][0] = new Node(maxLength);
}
for (int i = 0; i < mat[0].length; i++) {
mat[0][i] = new Node(maxLength);
}
int cur = 0;
int prev = 0;
for (int i = 1; i < arr.length+1; i++) {
cur = (cur + 1)%2;
prev = 1-cur;
for (int j = 1; j < mat[0].length; j++) {
if(arr[i-1] == sortArr[j-1]) {
mat[cur][j] = new Node(mat[prev][j-1]);
for (int k = 0; k < mat[cur][j].list.size(); k++) {
mat[cur][j].list.get(k)[mat[cur][j].localLength] = arr[i-1];
}
mat[cur][j].localLength++;
}
else {
if(mat[prev][j].localLength == mat[cur][j-1].localLength) {
if(mat[prev][j].localLength != 0) {
mat[cur][j] = new Node(mat[prev][j]);
for (int k = 0; k < mat[cur][j-1].list.size(); k++) {
boolean contains = false;
for (int k2 = 0; k2 < mat[prev][j].list.size() && !contains; k2++) {
contains = Arrays.equals(mat[cur][j-1].list.get(k), mat[prev][j].list.get(k2));
}
if(!contains) mat[cur][j].list.add(mat[cur][j-1].list.get(k));
}
}
else {
mat[cur][j] = new Node(maxLength);
}
}
else if(mat[prev][j].localLength > mat[cur][j-1].localLength) {
mat[cur][j] = new Node(mat[prev][j]);
}
else {
mat[cur][j] = new Node(mat[cur][j-1]);
}
}
}
}
return mat[cur][mat[0].length-1];
}
public void printLengthLIS() {
int length = LISLength();
System.out.println("LISs are " + length + " integers long.");
}
public void printNumOfLIS() {
int numOfLIS = buildMatrix().list.size();
System.out.println("There are " + numOfLIS + " LISs.");
}
public void printAllLIS() {
ArrayList<int[]> info = buildMatrix().list;
System.out.println("All the LISs are: ");
for (int i = 0; i < info.size(); i++) {
System.out.println(Arrays.toString(info.get(i)));
}
}
private class Node {
int localLength;
ArrayList<int[]> list;
public Node(int x) {
localLength = 0;
list = new ArrayList<int[]>();
list.add(new int[x]);
}
public Node(Node n) {
localLength = n.localLength;
list = new ArrayList<int[]>();
for(int[] temp : n.list) {
list.add(temp.clone());
}
}
}
And here is my testing code (with a lot of monitoring info):
for(int i = 10; i < 1000; i++) {
System.out.println("available memory BEFORE: " + java.lang.Runtime.getRuntime().maxMemory());
// creates a random array of different integers
Set<Integer> set = new HashSet<Integer>();
int temp = i;
while(temp != 0) {
if(set.add((int)(Math.random()*1000))) temp--;
}
int[] arr = new int[set.size()];
Iterator<Integer> it = set.iterator();
// into an actual array
for (int j = 0; j < arr.length; j++) {
arr[j] = it.next();
}
set = null;// supposed to free this memory allocation for the GC
System.out.println("i="+i+", length="+arr.length);
System.out.println(Arrays.toString(arr));
LIS test = new LIS(arr, arr.length+1);
test.printLengthLIS();
long startTime = System.currentTimeMillis();
test.printNumOfLIS();
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
System.out.println("It took me " + totalTime + " ms.");
test.printAllLIS();
test = null;
System.out.println("available memory AFTER: " + java.lang.Runtime.getRuntime().freeMemory());
}
[1,2,8,3,4,5,6,7,9]
[3,4,5,6,7,9]
or[1,2,3,4,5,6,7,9]
? Why? \$\endgroup\$