代码题(25)— 最大子序和、最长上升子序列
1、53. 最大子序和
给定一个整数数组 nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
class Solution { public: int maxSubArray(vector<int>& nums) { if(nums.empty()) return 0; int res = nums[0]; int sum = nums[0]; for(int i=1;i<nums.size();++i) { if(sum>0) sum += nums[i]; else sum = nums[i]; res = max(res, sum); } return res; } };
2、300. 最长上升子序列
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入:[10,9,2,5,3,7,101,18]
输出: 4 解释: 最长的上升子序列是[2,3,7,101],
它的长度是4
。
说明:
- 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
- 你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
方法一:时间复杂度为O(n2)
首先来看一种动态规划Dynamic Programming的解法,这种解法的时间复杂度为O(n2),类似brute force的解法,我们维护一个一维dp数组,其中dp[i]表示以nums[i]为结尾的最长递增子串的长度,对于每一个nums[i],我们从第一个数再搜索到i,如果发现某个数小于nums[i],我们更新dp[i],更新方法为dp[i] = max(dp[i], dp[j] + 1),即比较当前dp[i]的值和那个小于num[i]的数的dp值加1的大小,我们就这样不断的更新dp数组,到最后dp数组中最大的值就是我们要返回的LIS的长度.
class Solution { public: int lengthOfLIS(vector<int>& nums) { if(nums.empty()) return 0; int n = nums.size(); int res=0; vector<int> dp(n,1); for(int i=0;i<n;++i) { for(int j=0;j<i;++j) { if(nums[j]<nums[i]) dp[i]=max(dp[i],dp[j]+1); } res = max(res, dp[i]); } return res; } };
方法二:时间复杂度为O(nlogn)
思路是先建立一个空的dp数组,然后开始遍历原数组,对于每一个遍历到的数字,我们用二分查找法在dp数组找第一个不小于它的数字,如果这个数字不存在,那么直接在dp数组后面加上遍历到的数字,如果存在,则将这个数字更新为当前遍历到的数字,最后返回dp数字的长度即可,注意的是,跟上面的方法一样,特别注意的是dp数组的值可能不是一个真实的LIS。
class Solution { public: int lengthOfLIS(vector<int>& nums) { if(nums.empty()) return 0; vector<int> dp; for(int i=0;i<nums.size();++i) { int low=0,high=dp.size(); while(low<high) { int mid = (low+high)/2; if(dp[mid]<nums[i]) low = mid+1; else high=mid; } if(high>=dp.size()) dp.push_back(nums[i]); else dp[high] = nums[i]; } return dp.size(); } };