最近发现两个题目,用栈解决非常精巧,特此分享:
leetcode456:
给定一个整数序列:a1, a2, ..., an,一个132模式的子序列 ai, aj, ak 被定义为:当 i < j < k 时,ai < ak < aj。设计一个算法,当给定有 n 个数字的序列时,验证这个序列中是否含有132模式的子序列。
解法:
如果你从前往后去找的话,你需要
第一: 找到以后后面比前面大的数字
第二:找到一个位于这两个数字之间的数字,且位置在前两个位置之后
但是如果你从后往前找:
先找到ak<aj然后维护这个“最小值”尽可能大就好。一旦找到低于这个最小值的数就可以了。
我们要做大就是如何找到这个最小大最大值,画图可以看出来:
拿到B之后最小数值是A ->更新最小值,拿到c后最小值是B->更新最小值
D之后最小值是B->不更新最小值
E之后最小值是D更新最小值
总结就是,升则更新(pop)降则不更新,但是得记录(入栈)
![9be8f08ccf12fa8aa654fc7282986c8b.png](https://img-blog.csdnimg.cn/img_convert/9be8f08ccf12fa8aa654fc7282986c8b.png)
大概过程
![e562e6f97f30f160797ff4194dc9531a.png](https://img-blog.csdnimg.cn/img_convert/e562e6f97f30f160797ff4194dc9531a.png)
下面代码:
public
下一个题更是巧妙,虽然是前面的拓展
看一个无序数组中是否存在这样的序列
使得 i< j < k 且 a[i]<a[j]<a[k]
方法:栈
结构双向队列
先扫找到一个后面的数比前面的数大的情况
我们要找到一个数,他到后面有一个比他大的数,并且他的前面有一个比他小的数。
方法:
双向队列。
从后王前入队列,初始化入队,
如果比队尾元素小,入队,如果size等于3则返回true,否则继续
如果比队头大则替换队头不替换队尾,
如果比队头小,但是比队尾大,则替换队尾,
![8ba7dba7ec7dec9325c1174d57935b34.png](https://img-blog.csdnimg.cn/img_convert/8ba7dba7ec7dec9325c1174d57935b34.png)
public
第三个是数组的妙用:实际和队列很像,只是由于替换这一步,队列不方便,所以就用数组表示了。
求一个序列中最长的递增子序列:
方法:数组比较。下面链接是原文
Longest Increasing Subsequence Size (N log N) - GeeksforGeekswww.geeksforgeeks.org![9472d251bbcfd648c67faad260d3f874.png](https://img-blog.csdnimg.cn/img_convert/9472d251bbcfd648c67faad260d3f874.png)
比如序列 129045678
ans={1};
2>1 ans={1,2}
9>2 ans={1,2,9}
0<9->0<2->0<1 ans={0,2,9}
4<9->4>2->ans={0,2,4}
ans={0,2,4,5,6,7,8}
最后返回ans的size就好。
public static int solve(int[] a){
if(a.length==0)
return 0;
List<Integer> ans=new ArrayList<>();
ans.add(a[0]);
for(int i=1;i<a.length;i++){
if(a[i]>ans.get(ans.size()-1)){
ans.add(a[i]);
}else{
int loc=upper_bound(ans,a[i],0,ans.size()-1);
//去重
if(loc==0||(loc-1>=0&&ans.get(loc-1)!=a[i]))
ans.set(loc,a[i]);
}
}
return ans.size();
}
//find the loc
public static int upper_bound(List<Integer> ans,int target,int l,int r){
while(l<r){
int mid=l+(r-l)/2;
if(ans.get(mid)>target){
r=mid;
}else{
l=mid+1;
}
}
return l;
}