介绍窗口以及窗口内最大值或最小值的更新结构(单调双向队列)
1. 往右走,一个数进来,如果大,那么就弹出前面的,直到前面的数比他大或者为空,然后加在后面,如果比前面的数小,则加在后面;
2. 往左走,判断队列的最前面的index是否过期,如果过期,则弹出。
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
解法:
1.准备一个双端队列,向右走,如果比前面的大,则弹出前面的数,再添加到尾部,如果小,则直接添加到尾端
2. 如果index过期,则弹出顶部元素,此时栈顶元素为最大值。
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length==0){
return new int[0];
}
int index=0;
int len = nums.length;
LinkedList<Integer> list= new LinkedList<>();
int[] res = new int[len-k+1];
for(int i=0;i<len;i++){
while(!list.isEmpty()&&nums[list.peekLast()]<nums[i]){
list.pollLast();
}
list.offer(i);
if(list.peekFirst()==i-k){
list.pollFirst();
}
if(i>=k-1){
res[index++] = nums[list.peekFirst()];
}
}
return res;
}
最大值减去最小值小于或等于num的子数组数量
给定数组arr和整数num,共返回有多少个子数组满足如下情况:
max(arr[i..j])-min(arr[i..j])<=num
max()表示子数组的最大值,min()表示子数组的最小值
解法:
1.准备两个双端队列,一个保存最大值,一个保存最小值
2.从左往右走,判断最大队列和最小队列顶部元素的差值,如果不满足,则退出;
3.然后左边继续走;
这样能够让所有的情况的保存下来;
public int getNum(int[] nums, int num) {
if(nums==null||nums.length){
return 0;
}
LinkedList<Integer> qMax = new LinkedList<>();
LinkedList<Integer> qMin = new LinkedList<>();
int len = nums.length;
int L = 0;
int R = 0;
int res = 0;
while(L<len){
while(R<len){
while(!qMax.isEmpty()&&nums[qMax.peekLast()]<=nums[R]){
qMax.pollLast();
}
qMax.offerLast(R);
while(!qMin.isEmpty()&&nums[qMin.peekLast()]>=nums[R]){
qMin.pollLast();
}
qMin.offerLast(R);
if((nums[qMax.peekFirst()]-nums[qMin.peekFirst()])>num){
break;
}
R++;
}
res = res + R-L+1;
if(qMax.peekFirst()==L){
qMax.pollFirst();
}
if(qMin.peekFirst()==L){
qMin.pollFirst();
}
L++;
}
return res;
}
单调栈
1.准备一个堆栈,遍历整个数组,从左到右;
2.压栈,按照从大到小的顺序,如果遇到比栈顶元素大的话,则弹出栈顶元素;
此元素是右边大的元素,栈下的左边大的元素;
3.如果相等的话,则将下标合并,遇到大的,弹出,遇到小的继续压栈;
4.压到最后,然后逐渐弹出,对于最后一个元素,右边没有比它大的,左边大的就是下面的元素;
构造数组的MaxTree
一个数组的MaxTree定义如下。
数组必须没有重复元素。
MaxTree是一棵二叉树,数组的每一个值对应一个二叉树的节点。
包括MaxTree树在内且在其中的每一棵子树上,值最大的节点都是树的头。
有重复元素的数组arr,写出生成这个数组的MaxTree的函数,要求如果数组长度为N,且空间复杂度为O(N)
解法1:利用大根堆来搞,弄好了再连接起来
解法2:利用单调栈
1. 如果一个数的左右都没有大的数,那么此数作为根;
2. 如果只有其中一个,那么此数直接挂在上面
3. 如果有两个,那么选择小的,此数挂在小的上面
求最大子矩阵的大小
给定一个整型矩阵map,其中的值只有0和1两种,求其中全是1的所有矩阵中,最大的矩阵区域为1的数量
1.先讲解一个思路,将数组的数字想象成一个矩形;
2. 遍历数组,压栈,按照从小到大的顺序,遇到小的就弹出,遇到大的就继续加;
3. 遇到小的弹出时,如栈中是4,遇到3,要弹出4,此时3能够移动的距离就是2;
public static int maxRecFromBottom(int[] height){
if(height==null||height.length==0){
return 0;
}
int maxArea = 0;
Stack<Integer> stack = new Stack<>();
for(int i=0;i<height.length;i++){
while(!stack.isEmpty()&&height[i]<=height[stack.peek()]){
int j = stack.pop();
int k = stack.isEmpty()?-1:stack.peek();
int curArea = (i-k-1)*height[j];
maxArea = Math.max(curArea,maxArea);
}
stack.push(i);
}
while(!stack.isEmpty()){
int j = stack.pop();
int k = stack.isEmpty()?-1:stack.peek();
int curArea = (height.length-k-1)*height[j];
maxArea = Math.max(curArea,maxArea);
}
return maxArea;
}
4.那么回到原来的问题,我们想求一个矩阵中这样的情况,我们可以每一行进行处理;
public static int maxRecSize(int[][] map) {
if(map==null||map.length==0||map[0].length==0){
return 0;
}
int maxArea = 0;
int[] height = new int[map[0].length];
for(int i=0;i<map.length;i++){
for(int j=0;j<map[0].length;j++){
height[j] = map[i][j]==0?0:height[j]+1;
}
maxArea = Math.max(maxArea,maxRecFromBottom(height));
}
return maxArea ;
}
可见山峰问题
解法:先找到数组的最大值,然后利用单调栈,从小到大排序,遇到大的就弹出,
此时这个数的左右两个山峰都可以知道,为C(2,times) + 2*time
如果此时堆栈中只剩两个数,那么此时倒数第二个数是 C(2,times) + 2*time或者C(2,times) + 1*time
1的时候是最后一个数只有一个,2的时候是最后一个数只有两个