一.最长上升子序列
给定一个整数序列,找到最长上升子序列(LIS),返回LIS的长度。
最长上升子序列的定义:
最长上升子序列问题是在一个无序的给定序列中找到一个尽可能长的由低到高排列的子序列,这种子序列不一定是连续的或者唯一的。
https://en.wikipedia.org/wiki/Longest_increasing_subsequence
给出 [5,4,1,2,3]
,LIS 是 [1,2,3]
,返回 3
给出 [4,2,4,5,3,7]
,LIS 是 [2,4,5,7]
,返回 4
贪心策略:
时间复杂度:O(n*lg(n))
分析:开一个栈,每次取栈顶元素top和读到的元素temp做比较,如果temp > top 则将temp入栈;如果temp < top则二分查找栈中的比temp大的第1个数,并用temp替换它。 最长序列长度即为栈的大小top。
这也是很好理解的,对于x和y,如果x < y且Stack[y] < Stack[x],用Stack[x]替换Stack[y],此时的最长序列长度没有改变但序列Q的''潜力''增大了。
举例:原序列为1,5,8,3,6,7
栈为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终栈为1,3,6,7。最长递增子序列为长度4。
代码:
int longestIncreasingSubsequence(vector<int> nums) {
if(nums.size()==0)return 0;
// write your code here
int s[nums.size()];
int len=0;
s[len++]=nums[0];
for(int i=1;i<nums.size();i++)
{
int x=s[len-1];
if(nums[i]>x){
s[len++]=nums[i];
}
else
{
int l=0;
int r=len-1;
while(l<=r)
{
int m=(l+r)/2;
if(s[m]>=nums[i])r=m-1;
else l=m+1;
}
s[l]=nums[i];
}
}
return len;
}
暴力dp
class Solution {
public:
/**
* @param nums: The integer array
* @return: The length of LIS (longest increasing subsequence)
*/
int longestIncreasingSubsequence(vector<int> nums) {
if(nums.size()==0)return 0;
// write your code here
vector<int>dp(nums.size(),1);
int ret=1;
for(int i=0;i<nums.size();i++)
{
for(int j=0;j<i;j++)
{
if(nums[i]>nums[j])dp[i]=max(dp[i],dp[j]+1);
}
ret=max(dp[i],ret);
}
return ret;
}
};
二. Russian Doll Envelopes
You have a number of envelopes with widths and heights given as a pair of integers (w, h)
. One envelope can fit into another if and only if both the width and height of one envelope is greater than the width and height of the other envelope.
What is the maximum number of envelopes can you Russian doll? (put one inside other)
样例
Given envelopes = [[5,4],[6,4],[6,7],[2,3]]
,
the maximum number of envelopes you can Russian doll is 3 ([2,3] => [5,4] => [6,7]).
class Solution {
public:
/**
* @param envelopes a number of envelopes with widths and heights
* @return the maximum number of envelopes
*/
int maxEnvelopes(vector<pair<int, int>>& envelopes) {
// Write your code here
int len=envelopes.size();
vector<int>dp(len,1);
sort(envelopes.begin(),envelopes.end());
int res=1;
for(int i=0;i<len;i++)
for(int j=0;j<i;j++)
{
if(envelopes[i].first>envelopes[j].first&&envelopes[i].second>envelopes[j].second)
{
dp[i]=max(dp[i],dp[j]+1);
}
res=max(res,dp[i]);
}
return res;
}
};
2.转化为lis
先优先first 排序 再倒序排second,因为如果遇到(4,5),(5,4),(5,8),如果是顺序排second,(5,4)将会替换(4,5),如此一来,(5,8)就无法入栈。而如果倒序的话,(5,8)也就会先入栈,之后(5,4)才会替换(4,5)。即使first相等的数再多,他们也只是起到占位的作用,不会直接改变栈的长度。
class Solution {
public:
/**
* @param envelopes a number of envelopes with widths and heights
* @return the maximum number of envelopes
*/
int maxEnvelopes(vector<pair<int, int>>& envelopes) {
// Write your code here
vector<pair<int,int>>ret;
sort(envelopes.begin(),envelopes.end(),[](pair<int,int>a,pair<int,int>b)
{
if(a.first==b.first)return a.second>b.second;
else return a.first<b.first;
}
);
int len=1;
ret.push_back(envelopes[0]);
for(int i=1;i<envelopes.size();i++)
{
pair<int,int> ans=ret[len-1];
if(envelopes[i].second>ans.second){ret.push_back(envelopes[i]);len++;}
else
{
int l=0;
int r=len-1;
while(l<=r)
{
int mid=(l+r)/2;
if(ret[mid].second>=envelopes[i].second)r=mid-1;
else l=mid+1;
}
ret[l]=envelopes[i];
}
}
return len;
}
};
三. 寻找峰值
你给出一个整数数组(size为n),其具有以下特点:
- 相邻位置的数字是不同的
- A[0] < A[1] 并且 A[n - 2] > A[n - 1]
假定P是峰值的位置则满足A[P] > A[P-1]
且A[P] > A[P+1]
,返回数组中任意一个峰值的位置。
注意事项
数组可能包含多个峰值,只需找到其中的任何一个即可
给出数组[1
, 2, 1, 3, 4, 5, 7, 6]
返回1
, 即数值 2 所在位置, 或者6
, 即数值 7 所在位置.
这道题我一开始想复杂了,二分以后两边都考虑了,时间复杂度还是O(n),但后来仔细一想又发现这个数列刚开始是上升的,最后是下降的,所以中间一定有个波峰,那如果m处的值大于m-1处的值,说明在这一小段是单增的,而这个数列刚开始也是单增的,我们无法得知这块是不是数列刚开始的地方,然而这个数列末终将是要单减的,说明m后面肯定存在波峰,所以我们只用去考虑m后面的片段就可以了,时间复杂度也就变成了O(lgn);
int findPeak(vector<int> A) {
// write your code here
int l=0;
int r=A.size()-1;
return search(A,l,r);
}
int search(vector<int>A,int l,int r)
{
if(l<=r)
{
int m=(l+r)/2;
if(A[m]>A[m-1]&&A[m]>A[m+1])return m;
else if(A[m]>A[m-1]) return search(A,m+1,r);
else return search(A,l,m-1);
}
}