一、题目

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

点击查看原题

二、思路

1、动态规划

本题属于较为经典的动态规划,设立数组dp,dp[i]为以nums[i]结尾的递增子序列最长值。状态转移方程如下:
300. 最长递增子序列 - 图1
dp[i]的初始值为1.

2、贪心+二分

想让递增子序列最长,则需要让该序列尽可能增长最慢。
使用一个无序递增数组(无序指不按照nums中的序列,多个递增序列都放在一个数组中,这点下面会解释)来记录最长递增子序列。设定一个数组memo:

1)如果nums[i]>memo[memo.size()-1],则在memo末尾添加nums[i],代表递增子序列长度+1 2)如果nums[i]<=memo[memo.size()-1],说明nums[i]不能直接添加进目前的最长递增子序列中,二分查找以它为末尾的最长子序列长度,并覆盖memo中大于等于nums[i]的元素,如果后续会使用nums[i]所在序列,memo中的元素会慢慢被替代,直到长度超过当前的memo.size()。(这部分自己多写几次代码,手推代码去理解)

三、代码

1、动态规划

  1. class Solution {
  2. public int lengthOfLIS(int[] nums) {
  3. int[] dp = new int[nums.length];
  4. int ans = 1;
  5. for (int i = 0; i < nums.length; i++) {
  6. dp[i] = 1;
  7. for (int j = i-1; j >= 0; j--) {
  8. if (nums[i] > nums[j]) {
  9. dp[i] = Math.max(dp[i], dp[j] + 1);
  10. }
  11. }
  12. ans = Math.max(ans, dp[i]);
  13. }
  14. return ans;
  15. }
  16. }

时间复杂度为O(n),空间复杂度为O(n)。

2、贪心+二分

  1. class Solution {
  2. public int lengthOfLIS(int[] nums) {
  3. List<Integer> memo = new ArrayList();
  4. memo.add(nums[0]);
  5. for (int i = 1; i < nums.length; i++) {
  6. if (nums[i] > memo.get(memo.size()-1)) {
  7. memo.add(nums[i]);
  8. } else {
  9. int loc = find(memo, nums[i]);
  10. memo.set(loc, nums[i]);
  11. }
  12. }
  13. return memo.size();
  14. }
  15. public int find(List<Integer> memo, int num) {
  16. int start = 0, end = memo.size();
  17. while (start < end) {
  18. int mid = (start + end) / 2;
  19. if (memo.get(mid) == num) {
  20. return mid;
  21. } else if (memo.get(mid) > num) {
  22. end = mid;
  23. } else {
  24. start = mid + 1;
  25. }
  26. }
  27. return start;
  28. }
  29. }

时间复杂度为O(nlogn),空间复杂度为O(n)。