窗口
题目:求数组arry上的子数组个数,子数组从L到R上满足max-min<=num,arry和num为参数。
推论:1、一个子数组满足条件,则子数组内的任意子数组都满足条件。
max-min<=num
max2<=max
min2>=min
则max2-min2<=num
2、一个子数组不达标,向外扩,也不会达标。证明同理
思路:做两个双端队列,max和min,以0位置开头,向右扩展窗口,不达标时左端索引向右移动。
从0到X位置达标,则从0开始的子数组达标数为X-0+1;
从1到Y位置达标,则从1开始的子数组达标数为Y-1+1;
。。。。
public Integer getNum(int[] array, int num) {
int res = 0;
//两个双端队列,用于求最大值和最小值
LinkedList<Integer> max = new LinkedList<>();
LinkedList<Integer> min = new LinkedList<>();
//下标指针
int R = 0;
int L = 0;
while (L < array.length) {
while (R < array.length) {
// 双端队列操作
while (!min.isEmpty() && array[min.peekFirst()] >= array[R]) {
min.pollLast();
}
min.addLast(R);
while (!max.isEmpty() && array[max.peekFirst()] <= array[R]) {
max.pollLast();
}
max.addLast(R);
// 如果不达标,退出循环
if (array[max.getFirst()] - array[min.getFirst()] > num) {
break;
}
R++;
}
//R不达标时,求出个数
res += R - L;
// 查看数据是否过期,因为左指针要向右移动了。
if (min.peekFirst() == L) {
min.pollFirst();
}
if (max.peekLast() == L) {
max.pollFirst();
}
L++;
}
return res;
}
单调栈
压入,要求压入的数A要大于下面的数B(或小于),不满足就弹出B
解决问题,求一个数组中每个数的左边和右边比你小的且离你最近的数(无重复数)。
思路:当要压入A时,不满足条件,弹出一个数B,这个数的左边比他小的数就是他底下那个数C,右边比他小的数就是A,记录,继续比较,弹出、压入…如果没有数压入了,弹出时右边小数就没有,底下没数了,则左边小的数也没有。
复杂度O(n);
问题,求一个数组中每个数的左边和右边比你小的且离你最近的数(有重复数)。
public static int[][] getNearLess(int[] arr) {
int[][] res = new int[arr.length][2];
Stack<List<Integer>> stack = new Stack<>();
for (int i = 0; i < arr.length; i++) {
while (!stack.isEmpty() && arr[stack.peek().get(0)] > arr[i]) {
// 弹出整个list
List<Integer> pop = stack.pop();
//左边比他小的数的位置
int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);
for (Integer popi :
pop) {
res[popi][0] = leftLessIndex;
res[popi][1] = i;
}
}
if (!stack.isEmpty() && arr[stack.peek().get(0)] == arr[i]) {
stack.peek().add(Integer.valueOf(i));
} else {
ArrayList<Integer> list = new ArrayList<>();
list.add(i);
stack.push(list);
}
}
while (!stack.isEmpty()) {
List<Integer> pop = stack.pop();
int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);
for (Integer popi :
pop) {
res[popi][0] = leftLessIndex;
res[popi][1] = -1;
}
}
return res;
}
题:求数组的子数组L到R上的累加和
前缀和数组:
{1,3,6}
生成一个数组:数组中的值为0到这个位置下标的和。num:{1,4,10}
则L到R的累加和为num[R]-num[L-1}
题:在子数组中有X=(L到R的累加和)*(L到R中的最小值),求X的最大值。
推论:最小值不变时,子数组范围越大,X越大。
{3,4,6,2,8,6}
思路:找到以数组0位置为最小值的范围最大数组{3,4,6}——得到一个X1;
继续以数组1位置为最小值的范围最大数组{4,6}——得到一个X2;
…
算法:单调栈,求出两边比自己小的数的位置
理解单调性,以单调性为思路去解题;