题目

类型:动态规划
image.png

解题思路

https://labuladong.github.io/algo/3/23/67/
https://leetcode.cn/problems/longest-increasing-subsequence/solution/dong-tai-gui-hua-er-fen-cha-zhao-tan-xin-suan-fa-p/

代码

动态规划

  1. class Solution {
  2. public int lengthOfLIS(int[] nums) {
  3. // 定义:dp[i] 表示以 nums[i] 这个数结尾的最长递增子序列的长度
  4. int[] dp = new int[nums.length];
  5. // base case:dp 数组全都初始化为 1
  6. Arrays.fill(dp, 1);
  7. for (int i = 0; i < nums.length; i++) {
  8. for (int j = 0; j < i; j++) {
  9. if (nums[i] > nums[j])
  10. dp[i] = Math.max(dp[i], dp[j] + 1);
  11. }
  12. }
  13. int res = 0;
  14. for (int i = 0; i < dp.length; i++) {
  15. res = Math.max(res, dp[i]);
  16. }
  17. return res;
  18. }
  19. }

二分

  1. class Solution {
  2. public int lengthOfLIS(int[] nums) {
  3. int len = nums.length;
  4. if (len <= 1) {
  5. return len;
  6. }
  7. // tail 数组的定义:长度为 i + 1 的上升子序列的末尾最小是几
  8. int[] tail = new int[len];
  9. // 遍历第 1 个数,直接放在有序数组 tail 的开头
  10. tail[0] = nums[0];
  11. // end 表示有序数组 tail 的最后一个已经赋值元素的索引
  12. int end = 0;
  13. for (int i = 1; i < len; i++) {
  14. int left = 0;
  15. // 这里,因为当前遍历的数,有可能比有序数组 tail 数组实际有效的末尾的那个元素还大
  16. // 【逻辑 1】因此 end + 1 应该落在候选区间里
  17. int right = end + 1;
  18. while (left < right) {
  19. // 选左中位数不是偶然,而是有原因的,原因请见 LeetCode 第 35 题题解
  20. // int mid = left + (right - left) / 2;
  21. int mid = (left + right) >>> 1;
  22. if (tail[mid] < nums[i]) {
  23. // 中位数肯定不是要找的数,把它写在分支的前面
  24. left = mid + 1;
  25. } else {
  26. right = mid;
  27. }
  28. }
  29. // 因为 【逻辑 1】,因此一定能找到第 1 个大于等于 nums[i] 的元素
  30. // 因此,无需再单独判断,直接更新即可
  31. tail[left] = nums[i];
  32. // 但是 end 的值,需要更新,当前仅当更新位置在当前 end 的下一位
  33. if (left == end + 1) {
  34. end++;
  35. }
  36. }
  37. // 此时 end 是有序数组 tail 最后一个元素的索引
  38. // 题目要求返回的是长度,因此 +1 后返回
  39. end++;
  40. return end;
  41. }
  42. }