动态规划(时间复杂度 O(n^2))
/**
* return the longest increasing subsequence
* @param arr int整型一维数组 the array
* @return int整型一维数组
*/
/**
* 动态规划
* 状态 dp[i][2], dp[i][0]是以 arr[i] 为末尾的递增子序列的前一个数的索引
* dp[i][1]是以 arr[i] 为末尾的递增子序列的长度
* 状态转移方程:
* 从 arr[i] 前的数中,找到比 arr[i] 小且最小的arr[j],且以 arr[j] 为末尾的递增子序列最长,
* dp[i][0] = j, dp[i][1] = dp[j][1] + 1
* @param arr
* @return
*/
public int[] LIS (int[] arr) {
// write code here
if(arr.length == 0) return new int[0];
int[][] dp = new int[arr.length][2];
int maxInd = 0;
dp[0][0] = -1;
dp[0][1] = 1;
for (int i = 1; i < arr.length; i++){
int preInd = -1;
for (int j = 0; j < i; j++){
if(arr[j] < arr[i]){
if(preInd == -1)
preInd = j;
else {
// 若长度相同,取末尾数的字典序最小的
if(dp[j][1] > dp[preInd][1] || (dp[j][1] == dp[preInd][1] && arr[j] < arr[preInd]))
preInd = j;
}
}
}
dp[i][0] = preInd;
if(preInd == -1)
dp[i][1] = 1;
else
dp[i][1] = dp[preInd][1] + 1;
// 长度相同,得到字典序小的
if(dp[i][1] > dp[maxInd][1] || (dp[i][1] == dp[maxInd][1] && arr[i] < arr[maxInd])){
maxInd = i;
}
}
int[] res = new int[dp[maxInd][1]];
int i = dp[maxInd][1] - 1;
while (maxInd != -1){
res[i--] = arr[maxInd];
maxInd = dp[maxInd][0];
}
return res;
}
贪心 + 二分查找 (时间复杂度 O(nlogn))
/**
* 贪心 + 二分查找
* 维护一个数组 d[], d[i]是长度为i + 1的递增子序列的末尾元素的值
* 最开始时dp[0] = arr[0]
* maxLen[i]表示以arr[i]为结尾的递增子序列的最大长度
*
* 核心思想:
* 保证d[]中的元素都是符合条件的元素中的最小的那一个,即让d[]的递增趋势最慢,
* d[]的递增序列长度才能最大。
*
* @param arr
* @return
*/
public int[] LIS2 (int[] arr) {
if(arr.length == 0) return new int[0];
int[] d = new int[arr.length];
d[0] = arr[0];
int[] maxLen = new int[arr.length];
maxLen[0] = 1;
int len = 1;
for (int i = 1; i < arr.length; i++) {
if(arr[i] > d[len - 1]) {
d[len] = arr[i];
len++;
maxLen[i] = len;
}
else {
// 找到d中第一个比arr[i]大的元素,替换成arr[i]
int l = 0, r = len - 1;
int mid = (l + r) / 2;
/*
二分查找,区间左闭右闭,d[mid]即为d中第一个
比arr[i]大的元素。
*/
while (l < r){
if(d[mid] > arr[i]){
r = mid;
} else {
l = mid + 1;
}
mid = (l + r) / 2;
}
d[mid] = arr[i];
maxLen[i] = mid + 1;
}
}
int[] res = new int[len];
int curLen = len;
/*
从maxLen数组的末尾开始向前遍历,分别将
maxLen = curLen (len, len - 1, len - 2 ... 1)的元素
添加到res的指定位置中。
之所以从后向前遍历,是因为如果有多个maxLen相同的元素,则要得到
字典序较小的那个。
反证:
我们可以看到,当 maxLen[i] == max[i - n] && arr[i] > arr[i - n] 时,
因为arr[i] > arr[i - n]构成递增,则maxLen[i] > max[i - n]是必然的,
与我们的条件相违背。所以,从后往前遍历,我们始终能够得到maxLen相等的元素中
最小的那一个。
*/
for (int i = arr.length - 1; i >= 0; i--){
if(maxLen[i] == curLen){
res[--curLen] = arr[i];
}
}
return res;
}