【左神算法课】子数组最大差值小于某阈值,求满足条件的子数组个数

题目描述:

  

解法思路:

  本题其实是滑动窗口的变形。主体思路为:

  1.从第一个元素开始依次向后遍历,同时维护两个窗口(由于要同时操作窗口的头部和尾部,故采用双端队列):

      最大值窗口(递减),头部永远存最大值

      最小值窗口(递增),头部永远存最小值

  2.比较两个窗口的头部元素差值,若差值大于阈值,即可跳出内循环。

  3.跳出内循环后,检查头部元素是否过期,若过期,则清除。

复杂度:

  时间复杂度:O(n),注意虽然是两层循环,但元素只从滑动窗口尾部进,从头部清除,只是顺序扫描了一遍。

  空间复杂度:O(n),这里利用两个滑动窗口分别保存最大值和最小值。

代码:

  1 //C++版,注释部分为调试用,可忽略
  2 #include <iostream>
  3 #include <vector>
  4 #include <deque>
  5 #include <stdlib.h>
  6 using namespace std;
  7
  8 // void printArray(vector<int>& array) {
  9 //     if(array.empty()){
 10 //         cout<<"array is empty."<<endl;
 11 //         return ;
 12 //     }
 13 //     cout << array[0];
 14 //     for (int i = 1; i < array.size(); ++i)
 15 //         cout << " " << array[i];
 16 //     cout << endl;
 17 // }
 18
 19 // void printDeque(deque<int>& de) {
 20 //     if(de.empty()){
 21 //         cout<<"deque is empty."<<endl;
 22 //         return ;
 23 //     }
 24 //     cout << de.at(0);
 25 //     for (int i = 1; i < de.size(); ++i)
 26 //         cout << " " << de.at(i);
 27 //     cout << endl;
 28 // }
 29
 30 // vector<int> getRandomArray(int len) {
 31 //     if (len < 0) {
 32 //         exit(1) ;
 33 //     }
 34 //     vector<int> array(len);
 35 //     for (int i = 0; i < len; ++i) {
 36 //         array[i] = (int)(rand() % 10);
 37 //     }
 38 //     return array;
 39 // }
 40
 41 int getNum(vector<int> arr, int num) {
 42     if (arr.empty()) {
 43         return 0;
 44     }
 45     deque<int> qmin;
 46     deque<int> qmax;
 47     int i = 0;
 48     int j = 0;
 49     int res = 0;
 50     while (i < arr.size()) {
 51         while (j < arr.size()) {
 52             while (!qmin.empty() && arr[qmin.back()] >= arr[j]) {
 53                 qmin.pop_back();//cout<<"qmin.pop_back();"<<endl;
 54             }
 55             qmin.push_back(j);//cout<<"qmin.push_back(j);"<<endl;
 56             while (!qmax.empty() && arr[qmax.back()] <= arr[j]) {
 57                 qmax.pop_back();//cout<<"qmin.pop_back();"<<endl;
 58             }
 59             qmax.push_back(j);//cout<<"qmax.push_back(j);"<<endl;
 60             if (arr[qmax.front()] - arr[qmin.front()] > num) {
 61                 break;
 62             }
 63             ++j;
 64             // cout << "------in-----" << endl;
 65             // printDeque(qmin);
 66             // printDeque(qmax);
 67             // cout << "-----------" << endl;
 68         }
 69         // cout << "-----out1------" << endl;
 70         // printDeque(qmin);
 71         // printDeque(qmax);
 72         // cout << "-----------" << endl;
 73
 74         if (qmin.front() == i) {
 75             qmin.pop_front();
 76         }
 77         if (qmax.front() == i) {
 78             qmax.pop_front();
 79         }
 80
 81         // cout << "-----out2------" << endl;
 82         // printDeque(qmin);
 83         // printDeque(qmax);
 84         // cout << "-----------" << endl;
 85
 86         //cout << "j=" << j << "  i= " << i << endl;
 87         res += j - i;
 88         ++i;
 89     }
 90     return res;
 91 }
 92
 93 int main() {
 94     // vector<int> arr=getRandomArray(20);
 95     vector<int> arr{1, 2, 4, 3, 0, 7}; //test
 96     int num = 5;
 97     // printArray(arr);
 98     cout << getNum(arr, num) << endl;
 99     return 0;
100 }
 1 //Java版,左神给的代码
 2 package problems_2017_08_16;
 3
 4 import java.util.LinkedList;
 5
 6 public class Problem_02_AllLessNumSubArray {
 7
 8     public static int getNum(int[] arr, int num) {
 9         if (arr == null || arr.length == 0) {
10             return 0;
11         }
12         LinkedList<Integer> qmin = new LinkedList<Integer>();
13         LinkedList<Integer> qmax = new LinkedList<Integer>();
14         int i = 0;
15         int j = 0;
16         int res = 0;
17         while (i < arr.length) {
18             while (j < arr.length) {
19                 while (!qmin.isEmpty() && arr[qmin.peekLast()] >= arr[j]) {
20                     qmin.pollLast();
21                 }
22                 qmin.addLast(j);
23                 while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[j]) {
24                     qmax.pollLast();
25                 }
26                 qmax.addLast(j);
27                 if (arr[qmax.getFirst()] - arr[qmin.getFirst()] > num) {
28                     break;
29                 }
30                 j++;
31             }
32             if (qmin.peekFirst() == i) {
33                 qmin.pollFirst();
34             }
35             if (qmax.peekFirst() == i) {
36                 qmax.pollFirst();
37             }
38             res += j - i;
39             i++;
40         }
41         return res;
42     }
43
44     // for test
45     public static int[] getRandomArray(int len) {
46         if (len < 0) {
47             return null;
48         }
49         int[] arr = new int[len];
50         for (int i = 0; i < len; i++) {
51             arr[i] = (int) (Math.random() * 10);
52         }
53         return arr;
54     }
55
56     // for test
57     public static void printArray(int[] arr) {
58         if (arr != null) {
59             for (int i = 0; i < arr.length; i++) {
60                 System.out.print(arr[i] + " ");
61             }
62             System.out.println();
63         }
64     }
65
66     public static void main(String[] args) {
67         int[] arr = getRandomArray(30);
68         int num = 5;
69         printArray(arr);
70         System.out.println(getNum(arr, num));
71
72     }
73
74 }
时间: 2024-11-06 10:46:49

【左神算法课】子数组最大差值小于某阈值,求满足条件的子数组个数的相关文章

【左神算法课】二维矩阵的子矩阵最大累加和

题目描述: 思路描述(请结合后面的程序配套理解): 代码: 1 /* 2 本程序说明: 3 4 给定一个矩阵matrix,其中有正有负有0,返回子矩阵的最大累加和 5 例如矩阵matrix为: 6 -90 48 78 7 64 -40 64 8 -81 -7 66 9 其中最大累加和的子矩阵为 10 48 78 11 -40 64 12 -7 66 13 14 */ 15 #include <iostream> 16 #include <vector> 17 using names

左神算法书籍《程序员代码面试指南》——2_07将单向链表按某值划分成左边小、中间相等、右边大的形式

Problem:[题目] 给定一个单向链表的头节点head,节点的值类型是整型,再给定一个整数pivot. 实现一个调整链表的函数,将链表调整为左部分都是值小于 pivot的节点, 中间部分都是值等于pivot的节点,右部分都是值大于 pivot的节点. 除这个要求外,对调整后的节点顺序没有更多的要求. 例如:链表9->0->4->5->1,pivot = 3. 调整后链表可以是1->0->4->9->5, 可以是0->1->9->5-&g

左神算法书籍《程序员代码面试指南》——1_10最大值减去最小值小于或等于num的子数组数量

[题目]给定数组arr和整数num,共返回有多少个子数组满足如下情况:max(arr[i.j]) - min(arr[i.j]) <= num max(arfi.j])表示子数组ar[ij]中的最大值,min(arli.j])表示子数组arr[i.j]中的最小值.[要求]如果数组长度为N,请实现时间复杂度为O(N)的解法.[题解]使用两个单调栈,一个栈维持从大到小的排序,头部永远是最大值一个维持从小到大的排序,头部永远都是最小值然后使用窗口进行数据移动当右移后,最大最小差超过num时,计算这段数

左神算法第八节课:介绍递归和动态规划(汉诺塔问题;打印字符串的全部子序列含空;打印字符串的全排列,无重复排列;母牛数量;递归栈;数组的最小路径和;数组累加和问题,一定条件下最大值问题(01背包))

暴力递归: 1,把问题转化为规模缩小了的同类问题的子问题 2,有明确的不需要继续进行递归的条件(base case) 3,有当得到了子问题的结果之后的决策过程 4,不记录每一个子问题的解 动态规划 1,从暴力递归中来 2,将每一个子问题的解记录下来,避免重复计算 3,把暴力递归的过程,抽象成了状态表达 4,并且存在化简状态表达,使其更加简洁的可能 一:递归 1. 汉诺塔问题 汉诺塔问题(不能大压小,只能小压大),打印n层汉诺塔从最左边移动到最右边的全部过程. 左中右另称为 from.to.hel

左神算法进阶班1_5BFPRT算法

在无序数组中找到第k大的数1)分组,每N个数一组,(一般5个一组)2)每组分别进行排序,组间不排序3)将每个组的中位数拿出来,若偶数,则拿上 / 下中位数, 成立一个一个新数组.4)新数组递归调用BFPRT,则拿到整体的中位数num5)以num来划分整体数组,小于在左,大于在右边,使用[荷兰国旗方法]6)然后根据左右数组的规模,来确定进一步选择左右哪一部分:7)然后选择好后,继续 一:背景介绍在一大堆数中求其前k大或前k小的问题,简称TOP - K问题.而目前解决TOP - K问题最有效的算法即

左神算法进阶班3_1构造数组的MaxTree

题目 一个数组的MaxTree定义: 数组必须没有重复元素 MaxTree是一棵二叉树,数组的每一个值对应一个二叉树节点 包括MaxTree树在内且在其中的每一棵子树上,值最大的节点都是树的头 给定一个没有重复元素的数组arr,写出生成这个数组的MaxTree的函数,要求如果数组长度为N,则时间负责度为O(N).额外空间负责度为O(N). 实现思路 将数组按照大根堆进行排序 然后直接按照大根堆进行构造一颗二叉树即可. 使用单调栈 通过使用单调栈,将数组中中所有数的左右比他大的数记录下来 当某个数

左神算法进阶班5_1求二叉树中最大搜索子树大小

[题目] 给定一棵二叉树的头节点head,请返回最大搜索二叉子树的大小 [题解] 简化问题,想到该问题的解答应该有几种情形 第一种可能: 最大搜索二叉子树在head的左子树 第二种可能: 最大搜索二叉子树在head的右子树 第三种可能: 最大搜索二叉子树为自己:利用左子树的最大值与右子树的最小值 递归左子树,再递归右子树 信息1:左子树中最大搜索二叉树的大小 信息2:右子树中最大搜索二叉树的大小 信息3:左子树最大搜索二叉树的头结点 信息4:右子树最大搜索二叉树的头结点 信息5:左子树上的最大值

左神算法书籍《程序员代码面试指南》——1_06用栈来求解汉诺塔问题

[问题] 汉诺塔问题比较经典,这里修改一下游戏规则:现在限制不能从最左侧的塔直接移动到最右侧,也不能从最右侧直接移动到最左侧,而是必须经过中间.求当塔有N层的时候,打印最优移动过程和最优移动总步数.例如,当塔数为两层时,最上层的塔记为1,最下层的塔记为2,则打印:Move 1 from left to mid Move 1 from mid to right Move 2 from left to midMove 1 from right to mid Move 1 from mid to le

左神算法书籍《程序员代码面试指南》——2_12将搜索二叉树转换成双向链表

对二叉树的节点来说,有本身的值域,有指向左孩子和右孩子的两个指针:对双向链表的节点来说,有本身的值域,有指向上一个节点和下一个节点的指针.在结构上,两种结构有相似性,现在有一棵搜索二叉树,请将其转换为一个有序的双向链表. 1 #include <iostream> 2 #include <queue> 3 using namespace std; 4 struct treeNode 5 { 6 int v; 7 treeNode *l, *r; 8 treeNode(int a =