LeetCode-300 最长上升子序列
- 2020 年 3 月 14 日
- 筆記
LeetCode_300 最长上升子序列
description:
给定一个无序的整数数组nums,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
思路:
思路一:直接粗暴,O(n^2)动态规划
这个思路比较简单粗暴,定义dp[i]为以[0,i]为区间的,且以nums[i]为尾的最长上升子序列的值。循环遍历数组nums,当遍历到nums[i]时,dp[0]~dp[i-1]都已经算出来了。
那么状态转移方程为:
dp[i] = max(dp[j])+1;其中 0<=j<=i-1;
代码:
class Solution { public: int lengthOfLIS(vector<int>& nums) { int n = nums.size(); if(!n) return 0; int dp[n]; int ans = 1; for(int i=0;i<nums.size();i++){ dp[i] = 1; for(int j=0;j<i;j++){ if(nums[i]>nums[j]){ dp[i] = max(dp[i],dp[j]+1); ans = max(ans,dp[i]); } } ans = max(ans,dp[i]); } return ans; } };
复杂度分析:
时间复杂度:O(n^2),双层遍历
空间复杂度:O(n),需要一个dp数组
思路二:二分+贪心
这个思路比较tricky,不容易想到。
维护一个数组dp,dp[i]表示以i+1为最长上升子序列的最后一个元素,可以证明dp数组是严格递增的。
遍历nums,然后去dp中寻找第一个比nums大的数(这里用到二分查找,查找floor),然后替换它;如果nums>dp.back(),直接将nums添加到dp的末尾;
class Solution { public: int lengthOfLIS(vector<int>& nums) { int n = nums.size(); if(!n) return 0; vector<int> dp; dp.push_back(nums[0]); for(int i=1;i<n;i++){ if(nums[i]>dp.back()){ dp.push_back(nums[i]); }else{ int l = 0; int r = dp.size()-1; while(l<r){ int mid = l + (r-l)/2; if(dp[mid]<nums[i]){ l = mid+1; }else{ r = mid; } } dp[l] = nums[i]; } } return dp.size(); } };