タイトル説明
解決
重要:終了配列と添え字の意味を理解してください。
長さi + 1の各サブシーケンスの最後の要素の最小値をインデックスiで記録します。これにより、サブシーケンスを可能な限り拡張できます。つまり、end [i]は長さi +の増加するすべてのサブシーケンスの末尾を表します。 1最小要素を持つシーケンス
int [] end = new int [arr.length];
int len = 0; // end array end [len]は、実際の長さがlen + 1
int [である文字列の最小終了要素を記録します。] dp = new int [arr.length]; // iで終わるサブシーケンスの最大長を記録します
import java.util.*;
public class Solution {
/**
* retrun the longest increasing subsequence
* @param arr int整型一维数组 the array
* @return int整型一维数组
*/
public int[] LIS (int[] arr) {
if(arr==null || arr.length<1) return arr;
// write code here
//记录索引为i的长度为i+1的每个子序列最后一个元素的最小值,尽可能地使得子序列可以扩展,即end[i]表示的是所有长度为i+1的递增子序列中尾部元素最小的那个序列
int[] end = new int[arr.length];
//最后我们可以得知该序列是严格递增的,所以方便之后的二分
int[] dp = new int[arr.length];//记录以i为结尾的子序列的最大长度
end[0] = Integer.MAX_VALUE;
int len=0;//end数组end[len]记录实际长度为len+1的字串的最小结尾元素
for(int i=0;i<arr.length;i++){
if(arr[i]>end[len]){
//当前的字串长度是否可以扩展,大于则可以扩展,且不需要调整end数组
len++;
end[len] = arr[i];
//记录当前元素结尾的最长字串的长度
dp[i] = len+1;//len+1映射实际的长度
}else{
//不能扩充end的长度,需要调整end数组,使得对应每个i+1长度的字序列最后一个元素尽量小
//调整
int left = 0,right = len;//使用二叉查找找到第一个比arr[i]大的数,注意这里的end[len]里面已经存在数了,所以下面为left<=end
//找到最后一个比arr[i]小的数,选择最大左边界和最小右边界
while(left<=right){
//[left,right)
int mid = (left+right)/2;
if(end[mid]>arr[i]){
right = mid-1;//这里注意,我们如果找到了第一个比他大的,则替换它
}else{
left = mid+1;
}
}
end[left] = arr[i];//填入一个,更新end数组
dp[i] = left+1;//当前以arr[i]为结尾的最大字串长度更新
}
}
//此时的end数组里面记录len则为最大长度的而且我们应该找字典序最小的
int []result = new int[len+1];
for(int i=arr.length-1;i>=0;i--){
if(dp[i]==len+1){
//dp[i]记录的是以i为结尾元素的最长递增字串长度
result[len] = arr[i];
len--;
}
}
return result;
}
}