题目描述和难度
- 题目描述:
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入:[10,9,2,5,3,7,101,18]
输出: 4 解释: 最长的上升子序列是[2,3,7,101],
它的长度是4
。
说明:
- 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
- 你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
- 题目难度:中等。
- 英文网址:300. Longest Increasing Subsequence 。
- 中文网址:300. 最长上升子序列 。
思路分析
求解关键:
思路1:使用动态规划。
状态的定义:以 num[i] 结尾的最长上升子序列的长度。
状态转移方程:之前的数中比 num[i] 小的最长上升子序列的长度 + 1。
思路2:使用贪心选择 + 二分查找算法。
参考解答
参考解答1
import java.util.Arrays;
// 最长上升子序列问题
// 300. 最长上升子序列
// https://leetcode-cn.com/problems/longest-increasing-subsequence/description/
public class Solution {
//【关键】将 dp 数组定义为:以 nums[i] 结尾的最长上升子序列的长度
// 那么题目要求的,就是这个 dp 数组中的最大者
// 以数组 [10, 9, 2, 5, 3, 7, 101, 18] 为例:
// dp 的值: 1 1 1 2 2 3 4 4
// 注意实现细节。
public int lengthOfLIS(int[] nums) {
int len = nums.length;
if (len == 0) {
return 0;
}
// 状态的定义是:以 i 结尾的最长上升子序列的长度
// 状态转移方程:之前比最后那个数字小的最长上升子序列的长度 + 1
int[] dp = new int[len];
Arrays.fill(dp, 1); // 如果只有 1 个元素,那么这个元素自己就构成了最长上升子序列,所以设置为 1 是合理的
for (int i = 1; i < len; i++) { // 从第 2 个元素开始,逐个写出 dp 数组的元素的值
int curVal = nums[i];
for (int j = 0; j < i; j++) { // 找出比当前元素小的哪些元素的最小值
if (curVal > nums[j]) {
dp[i] = Integer.max(dp[j] + 1, dp[i]);
}
}
}
// 最后要全部走一遍,看最大值
int res = dp[0];
for (int i = 0; i < len; i++) {
res = Integer.max(res, dp[i]);
}
return res;
}
public static void main(String[] args) {
int[] nums = {10, 9, 2, 5, 3, 7, 101, 18};
Solution solution = new Solution();
int lengthOfLIS = solution.lengthOfLIS(nums);
System.out.println(lengthOfLIS);
}
}
参考解答2
import java.util.Arrays;
public class Solution4 {
// 有贪心选择的意思
public int lengthOfLIS(int[] nums) {
int len = nums.length;
// 先考虑极端输入
if (len <= 1) {
return len;
}
// tail 数组的定义:长度为 i+1 的上升子序列的末尾最小是几
int[] tail = new int[len];
// 遍历一遍整个数组,使用二分查找算法
tail[0] = nums[0];
int res = 0;
for (int i = 1; i < len; i++) {
// 比 tail 数组实际有效的末尾的那个元素还大
if (nums[i] > tail[res]) {
// 直接添加在那个元素的后面
tail[++res] = nums[i];
} else {
// 二分查找到第 1 个比 nums[i] 还大的元素,更新到那个位置
int l = 0;
int r = res;
while (l < r) {
int mid = l + (r - l) / 2;
// 有就啥都不做了
if (tail[mid] == nums[i]) {
l = mid;
break;
} else if (tail[mid] >= nums[i]) {
r = mid;
} else {
l = mid + 1;
}
}
tail[l] = nums[i];
}
printArray(nums[i], tail);
}
return ++res;
}
// 调试方法,以观察是否运行正确
private void printArray(int num, int[] tail) {
System.out.print("当前数字:" + num);
System.out.print("\t当前 tail 数组:");
int len = tail.length;
for (int i = 0; i < len; i++) {
if (tail[i] == 0) {
break;
}
System.out.print(tail[i] + ", ");
}
System.out.println();
}
public static void main(String[] args) {
int[] nums = new int[]{3, 5, 6, 2, 5, 4, 19, 5, 6, 7, 12};
Solution4 solution4 = new Solution4();
int lengthOfLIS = solution4.lengthOfLIS(nums);
System.out.println("最长上升子序列的长度:" + lengthOfLIS);
}
}
本篇文章的地址为 https://liweiwei1419.github.io/leetcode-solution/leetcode-0300-longest-increasing-subsequence ,如果我的题解有错误,或者您有更好的解法,欢迎您告诉我 liweiwei1419@gmail.com 。
.label-warning {
background-color: #f0ad4e;
}
.label-success {
background-color: #5cb85c;
}
.label-danger {
background-color: #d9534f;
}
.label {
display: inline;
padding: .2em .6em .3em;
font-size: 75%;
font-weight: 700;
line-height: 1;
color: #fff;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25em;
}