经典的LIS问题的最简形态(求LIS长度)
核心逻辑是:
  mem[n]: 长度为n+1的LIS的最后一个数的最优值(i.e. 最小).
  
有O(n^2)和O(nlogn)时间两种解。
import java.util.*;

// time O(nlogn) solution
// 视频讲解 https://www.youtube.com/watch?v=l2rCz7skAlk
public class Solution {
    public int LIS (int[] arr) {
      if (arr.length <=1 ) return arr.length;

      // mem[i]: 长度为i+1的LIS的最后一个数的最优值(i.e. 最小).
      // LIS.length<=arr.length, 所以就用arr.length去初始
      int[] mem = new int[arr.length];
      // 初始: LIS = { arr[0] }
      mem[0] = arr[0];
      int lenLIS = 1;
      
      for (int i = 1; i < arr.length; i++) {
        int num = arr[i];
        // search for num in mem[0, lenLIS-1]
        // note: mem[0, lenLIS-1] is already sorted
        // 请务必参照Arrays.binarySearch的documentation
        // 我只能说用Java刷题的咱们都是上辈子折翼的天使
        int result = Arrays.binarySearch(mem, 0, lenLIS, num);
        
        // result>=0表示found,那么插不插入都一样
        if (result >= 0) continue;
        
        // else,result = -insertionPoint - 1
        int insertionPoint = -1-result;
        mem[insertionPoint] = num;  // replace
        // num大于mem[0, lenLIS-1]里的所有数, 
        // i.e. 之前的replace其实是个append, 所以lenLIS++
        if (insertionPoint == lenLIS) lenLIS++;
      }
      
      return lenLIS;
    }
}
import java.util.*;

// time O(n^2) solution
public class Solution {
    // dp[i]: length of LIS ending at i.
    //  = MAX{ dp[j] + 1 }, for j < i && arr[j] < arr[i]
    public int LIS (int[] arr) {
      int[] dp = new int[arr.length];
      Arrays.fill(dp, 1); // dp[i] is at least 1, i.e. 1 element
      int maxLen = 0;
      
      for (int i = 0; i < arr.length; i++) {
        for (int j = 0; j < i; j++) {
          if (arr[j] < arr[i]) {
            dp[i] = Math.max(dp[i], dp[j]+1); 
          }
        }
        maxLen = Math.max(maxLen, dp[i]);
      }
      
      return maxLen;
    }
}