package com.heu.wsq.niuke.top200;
/**
* 最长递增子序列
* @author wsq
* @date 2021/6/1
* 描述
* 给定数组arr,设长度为n,输出arr的最长递增子序列。(如果有多个答案,请输出其中字典序最小的)
*
* 示例1
* 输入:
* [2,1,5,3,6,4,8,9,7]
* 返回值:
* [1,3,4,8,9]
*
* 示例2
* 输入:
* [1,2,8,6,4]
* 返回值:
* [1,2,4]
*
* 说明:
* 其最长递增子序列有3个,(1,2,8)、(1,2,6)、(1,2,4)其中第三个字典序最小,故答案为(1,2,4)
*/
public class LIS {
/**
* 动态规划 + 二分查找
* 1. 采用动态规划,需要O(n2)的时间复杂度,该题会超时
* 2. 超时的原因在于 搜索 比当前位置小的元素位置,从而搜当前元素的最大上升序列长度
* 3. 维持一个最小升序的字典序数组,采用二分查找的方式找到新元素可插入的位置,从而计算出当前位置
* 最长上升序列长度。
* @param arr
* @return
*/
public int[] LIS(int[] arr){
if(arr == null || arr.length == 0){
return null;
}
int n = arr.length;
int[] f = new int[n];
int[] end = new int[n];
int len = 0;
for(int i = 0; i < n; i++){
if(len == 0 || end[len - 1] < arr[i]){
end[len++] = arr[i];
f[i] = len;
}else{
// 最难的点就在于这里,维护一个递增、升序的最小字典序,
// 并通过二分查找的方式获取新元素的位置
int idx = binarySearch(end, 0, len - 1, arr[i]);
end[idx] = arr[i];
f[i] = idx + 1;
}
}
// 回溯获得最长上升序列
int[] ans = new int[len];
for(int i = n-1; i >= 0; i--){
if(f[i] == len){
ans[--len] = arr[i];
}
}
return ans;
}
private int binarySearch(int[] end, int left, int right, int v){
int i = left, j = right;
while(i <= j){
int mid = (i + j) >> 1;
if(end[mid] == v){
return mid;
}else if(end[mid] < v){
i = mid + 1;
}else{
j = mid - 1;
}
}
return i;
}
}
最长递增子序列(动态规划+二分查找)
最新推荐文章于 2023-06-01 17:14:26 发布