題目一:區間子數組個數
給定一個元素都是正整數的數組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 ; } };