题目一:区间子数组个数
给定一个元素都是正整数的数组A
,正整数 L
以及 R
(L <= R
)。
求连续、非空且其中最大元素满足大于等于L
小于等于R
的子数组个数。
例如 : 输入: A = [2, 1, 4, 3] L = 2 R = 3 输出: 3 解释: 满足条件的子数组: [2], [2, 1], [3].
注意:
- L, R 和
A[i]
都是整数,范围在[0, 10^9]
。 - 数组
A
的长度范围在[1, 50000]
。
思路:比较简单,维护住子数组中的那个最大值就行了,如果这个最大值超过了上界,那么直接break,因为再扩大子数组也是徒劳
class Solution { public: int numSubarrayBoundedMax(vector<int>& A, int L, int R) { int tempMax ; int count = 0 ; for(int i=0;i<A.size();i++){ tempMax = -1 ; for(int j=i;j<A.size();j++){ if(A[j]>tempMax){ tempMax = A[j] ; } if(L<=tempMax&&tempMax<=R){ count++ ; }else{ if(tempMax>R)break ; } } } return count ; } };
题目二:最长上升子序列
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入:[10,9,2,5,3,7,101,18]
输出: 4 解释: 最长的上升子序列是[2,3,7,101],
它的长度是4
。
说明:
- 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
- 你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
思路:经典题目,用动态规划可以到n^2的时间复杂度,但是动态规划我老想不出来,总觉得状态转移方程比较难想,多积累吧
动态规划的做法,dp数组中存,每一个以dp[i]结尾的最长上升子序列,更新的时候:
if(nums[i]>nums[j]){ dp[i] = max(dp[i],dp[j]+1) ; }
整个代码:
class Solution { public: int lengthOfLIS(vector<int>& nums) { if(nums.size()<=1){ return nums.size() ; } int ans = 1; vector<int> dp(nums.size(),1) ; for(int i=1;i<nums.size();i++){ for(int j=0;j<i;j++){ if(nums[i]>nums[j]){ dp[i] = max(dp[i],dp[j]+1) ; } } } for(int i=0;i<nums.size();i++){ ans = max(ans,dp[i]) ; } return ans ; } };
题目中还说了,有nlogn的算法,网上查了一下,这篇博客说的比较好:https://blog.csdn.net/will130/article/details/50575967
具体思路是,维护一个上升序列,每次有元素进来,要么直接加后面,要么更新前面第一个比它大的数,这里简单搬运一下
class Solution { public:// int lengthOfLIS(vector<int>& nums) { int n=nums.size(); if(n <= 1) return n; //tail[i]表示长度为i的递增序列末尾的数字 //tail[]数组性质:tail[0]<tail[1]<...tail[n] !!! vector<int> tail(n);//初始化为n个值为0的元素 //1.len为当前最长的递增序列长度(为方便操作将len减1,从0开始,最后再加上1) int len=0; tail[0]=nums[0]; //2.每次读入一个新元素nums[i] for(int i=1;i<n;i++) {//遍历nums[]中的数 if(nums[i] < tail[0]) {//(1)nums[i]比所有递增序列的末尾都小,则长度为1的序列更新为这个更小的末尾。 tail[0]=nums[i]; } else if(nums[i] > tail[len]) {//(2)nums[i]比所有序列的末尾都大,则直接将nums[i]加到后面 tail[++len]=nums[i]; } else {//(3)在中间,则更新那个末尾数字刚好大于等于nums[i]的那个序列,nums[i]替换其末尾数字 tail[biSearch(tail, 0, len, nums[i])]=nums[i]; } } return len+1; } int biSearch(vector<int>& tail, int low, int high, int target) {//由于tail数组是有序的,故可二分查找其中元素 while(low <= high)//不能是low<high {//当low=high时还要进行一次循环!!! //此时mid=low=high.若tail[mid]<target,则low=mid+1.而不是直接返回low!!! int mid = low + (high-low)/2; if(tail[mid] == target) return mid; else if(tail[mid] > target) { high=mid-1; } else { low=mid+1; } } return low; } };
题目三:乘积最大子序列
给定一个整数数组 nums
,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1] 输出: 0 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
思路:这个题确切的来说应该是子数组,跟最长上升子序列就动归而言有点像,由于是子数组不是子序列,所以可以o(n)完成(子序列需要遍历状态i之前的所有状态,而子数组是连续的,子需要记录上一个状态就行)
但是由于是乘法,所以有符号的问题,上一个状态是负的,乘上一个负数反而可能变的很大,所以这道题的关键是维护两个状态,当前最大和当前最小,每次计算都更新这两个状态,不过只有最大值和ans比较
class Solution { public: int maxProduct(vector<int>& nums) { int len = nums.size() ; if(len==0){ return 0 ; } int tempMax = nums[0] ; int tempMin = nums[0] ; int ans = nums[0] ; int lastMax = nums[0]; for(int i=1;i<len;i++){ tempMax = max(max(lastMax*nums[i],nums[i]),tempMin*nums[i]) ; tempMin = min(min(lastMax*nums[i],nums[i]),tempMin*nums[i]) ; lastMax = tempMax ; if(tempMax>ans){ ans = tempMax ; } } return ans ; } };