法一(动态规划)
/**
* 法一(动态规划)
* 1. 步骤
* (1)确定dp数组以及下标的含义
* dp[i]表示以第i个数字为结尾的最长上升子序列的长度
* (2)确定递推公式
* 位置i的最长升序子序列等于j从0到i-1各个位置的最长升序子序列+1的最大值
* if(nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1)
* (3)确定dp数组如何初始化
* 每一个i,对应的dp[i]起始大小至少都是是1
* (4)确定遍历顺序
* dp[i]由0到i-1各个位置的最长升序子序列推导而来,则遍历i一定是从前向后遍历
* 2. 复杂度
* (1)时间复杂度 0(n^2)
* (2)空间复杂度 0(1)
*
* @param nums
* @return
*/
public int lengthOfLIS(int[] nums) {
int max = 1;
int dp[] = new int[nums.length];
Arrays.fill(dp, 1);
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1); // 取长的子序列
}
}
max = Math.max(max, dp[i]);
}
return max;
}
法二(动态规划 + 二分查找)
/**
* 法二(动态规划 + 二分查找)
* 1. 思路
* (1)dp[i]表示长度为i+1的所有上升子序列末尾的最小值
* (2)新来一个数字以后,去寻找dp中第一个比它大的值,然后将其更新为新来数
* (3)如果dp中没有比新来数大的数,那么就扩展长度,将新来数放到最后
* (4)通过上边dp的定义,dp一定是有序的,要从一个有序数组中寻找第一个大于等于新来数的位置,此时就可以通过二分查找了
* 2. 复杂度
* (1)时间复杂度 0(nlogn)
* (2)空间复杂度 0(1)
*
* @param nums
* @return
*/
public int lengthOfLIS_2(int[] nums) {
int max = 0;
int dp[] = new int[nums.length];
for (int num : nums) {
int left = 0, right = max;
while (left < right) { // 寻找dp中第一个大于等于新来数的位置
int mid = left + (right - left) / 2;
if (dp[mid] < num) {
left = mid + 1;
} else {
right = mid;
}
}
dp[left] = num; // 将该位置的数更新为新来数
if (left == max) { // dp中没有比新来数大的数,则扩展长度
max++;
}
}
return max;
}
本地测试
/**
* 300. 最长递增子序列
*/
lay.showTitle(300);
Solution300 sol300 = new Solution300();
int[] nums300 = new int[]{10, 9, 2, 5, 3, 7, 101, 18};
System.out.println(Arrays.toString(nums300));
System.out.println(sol300.lengthOfLIS(nums300));
System.out.println(sol300.lengthOfLIS_2(nums300));