爪哇中的滑动窗口最大值
在这篇文章中,我们将看到有关Java滑动窗口最大值的信息
问题
给定一个整数数组和一个整数k,从大小为K的所有连续子数组中找到的最大元素。
例如:
整数k = 3
输出 :6,6,4,4,4,5
对于每个大小为k的子数组,打印其最大元素。
解
天真的方法:
基本的解决方案是只生成所有大小为k的连续子数组,然后遍历它们,以找出当前子数组中的最大值。
考虑到,对于每个景点,我们基本上都在考虑‘k’元素,然后我们循环遍历这k个元素,因此该算法最差的时间复杂度是 O(n * k).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
包 数组; 进口 爪哇.实用程序.扫描器; 上市 类 slideWindowMax { 上市 静态的 虚空 主要(串[] args) { 扫描器 scn = 新 扫描器(系统.在 ); 整型 [] rr = 新 整型[scn.nextInt()]; 对于(整型 i = 0; i < rr.长度; i++) { rr[i] = scn.nextInt(); } 整型 windowSize = scn.nextInt(); 解决(rr, windowSize); } 上市 静态的 虚空 解决(整型[] rr, 整型 k) { //从k开始外循环并运行直到 //当前指针等于arr.length 对于(整型 i = k; i <= rr.长度; i++) { 整型 最高 = 整数.MIN_VALUE; //此循环考虑以i-1结尾的大小为k的子数组 对于(整型 j = i-k; j<i; j++) { 最高 = 数学.最高(最高, rr[j]); } 系统.出.打印(最高); } } } |
稍微有效的方法:
通过使用,我们可以肯定地减少为每个子数组找到最大值所需的时间 段树。
我们可以为给定的数组实现一个分段树,并且可以通过范围查询获得每个子数组的最大值 [i,i + k-1].
- 段树中的节点总数 :
构建段树的最差时间复杂度是 上) 因为我们知道
(i)段树的叶节点包含数组的所有元素。
(ii)最后一级上的节点数是所有上一级上的节点数。
- 数学上,
- 考虑数组的长度为n,因此,段树的叶节点将为n。
- 因此,所有较高级别上的节点数将为n-1。
- 长度为n的数组的分段树上的总节点数为:
Tn =叶节点+上层节点
= n + n-1
= 2n+1
- 复杂度分析
我们的分段树的构造只涉及每个节点一次的计算,因此分段树构造的最差时间复杂度为O(2n + 1),即 上)。
每个子数组的范围查询结果将在 O(logk).
查询计算将针对所有‘n-k+1’大小为k的子数组。
因此,此算法的总体时间复杂度将为O((n-k + 1)* 日志k),即 O(nlogk).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
包 数组; 进口 爪哇.实用程序.扫描器; 上市 类 slideWindowMax { 静态的 整型[] 萨尔; 上市 静态的 虚空 主要(串[] args) { 扫描器 scn = 新 扫描器(系统.在 ); 整型[] rr = 新 整型[scn.nextInt()]; 对于 (整型 i = 0; i < rr.长度; i++) { rr[i] = scn.nextInt(); } 整型 windowSize = scn.nextInt(); 整型 高度 = (整型)数学.细胞((数学.日志(rr.长度) / 数学.日志(2))); / *段数组的大小 即节点数将为= [(2 ^ 高度 + 1)-1] * / 萨尔 = 新 整型[1<<高度 -1]; 构造(0, 0, rr.长度-1, rr); 解决(rr, windowSize); } 上市 静态的 虚空 解决(整型[] rr, 整型 k) { 对于 (整型 i = 0; i <= rr.长度 - k; i++) { / *查找从i到i + k的范围查询的结果,这基本上是一个子数组。 * * / 系统.出.打印(询问(0, i, i + k - 1, 0, rr.长度 - 1)); } } 上市 静态的 整型 构造(整型 IDX, 整型 开始, 整型 结束, 整型[] rr) { / *叶子节点包含数组元素* / 如果 (开始 == 结束) { 萨尔[IDX] = rr[结束]; 返回 萨尔[IDX]; } 整型 中 = (开始 + 结束) / 2; / *将分段树中每个节点的范围分成两半* / 整型 剩下 = 构造(2 * IDX + 1, 开始, 中, rr); 整型 对 = 构造(2 * IDX + 2, 中 + 1, 结束, rr); / *将计算段树中当前索引的结果 * 以后期顺序排列,并且最多为两个孩子。 * / 萨尔[IDX] = 数学.最高(剩下, 对); 返回 萨尔[IDX]; } 上市 静态的 整型 询问(整型 IDX, 整型 询问Start, 整型 QueryEnd, 整型 开始, 整型 结束) { / *如果我们的范围完全不在查询范围内, *我们需要返回一个结果,使其对我们的最终答案没有影响。 * / 如果 (开始 > QueryEnd || 结束 < 询问Start) { 返回 整数.MIN_VALUE; } / *如果当前段的范围完全落入 * 在查询中然后返回其值。 * / 其他 如果 (开始 >= 询问Start && 结束 <= QueryEnd) { 返回 萨尔[IDX]; } 其他 { 整型 中 = (开始 + 结束) / 2; 整型 剩下 = 询问(2 * IDX + 1, 询问Start, QueryEnd, 开始, 中); 整型 对 = 询问(2 * IDX + 2, 询问Start, QueryEnd, 中 + 1, 结束); 返回 数学.最高(剩下, 对); } } } |
最有效的方法 :
在这种方法中,我们使用 双端队列 这有助于我们找到最大的滑动窗口 上).
- A 双端队列 基本上是一个队列,该队列在入队和出队都在两端开放,也就是说,您可以从前面或后面添加或删除元素。
我们实际上要解决的问题是:
我们将子数组的k个元素按相反的顺序保留,我们不需要保留所有k个元素,尽管稍后我们将在代码中看到。
- 为前k个元素生成双端队列,使它们以相反的顺序排序,以使最大元素位于最前面。
- 如果Deque为空,则直接添加元素,否则检查传入的元素是否大于最后一个元素,如果是,则从最后弹出元素,直到剩余Deque的最后一个元素大于传入的元素。
- 我们还需要删除哪些元素 属于不同的子数组。即双端队列的索引必须在该范围内, [i,i + k].
一个元素将仅在以下两种情况下被删除:
(一世) 如果即将到来的元素更大 than the element at 后,如果这样,它将继续弹出该元素,直到剩余出队的后面有一个更大的元素为止,因为我们需要保持数组以相反的顺序排序。
(ii)如果元素属于 任何其他子数组 那么就没有必要保留它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
包 组织.Arpit.爪哇2blog; 进口 爪哇.实用程序.链表; 进口 爪哇.实用程序.扫描器; 上市 类 滑动窗口最大 { 静态的 整型[] 萨尔; 上市 静态的 虚空 主要(串[] args) { 扫描器 scn = 新 扫描器(系统.在 ); 整型[] rr = 新 整型[scn.nextInt()]; 对于 (整型 i = 0; i < rr.长度; i++) { rr[i] = scn.nextInt(); } 系统.出.打印(“ rr []:{”); 对于 (整型 i = 0; i < rr.长度; i++) { 系统.出.打印(”+rr[i]); } 系统.出.打印(“}”); 整型 windowSize = scn.nextInt(); 解决效率(rr, windowSize); } 上市 静态的 虚空 解决效率(整型[] rr, 整型 k) { 链表<整数> 双端队列 = 新 链表<>(); 对于 (整型 i = 0; i < rr.长度; i++) { / *继续从双端队列移除元素 *小于当前元素, *因为我们需要保持双端队列按dec顺序排序 * / 而 (!双端队列.是空的() && rr[双端队列.getLast()] <= rr[i]) { 双端队列.removeLast(); } / *删除i-k元素,因为该元素不属于 *到我们当前正在处理的子数组。 * / 而 (!双端队列.是空的() && 双端队列.getFirst() <= i - k) { 双端队列.removeFirst(); } 双端队列.addLast(i); 如果(i >= k-1) { / *仅在我们处理了至少k个元素后才打印 *制作第一个子数组 * / 系统.出.打印(”+rr[双端队列.getFirst()]); } } } } |
当您运行上述程序时,将获得以下输出:
2 6 -1 2 4 1 -6 5
rr []:{2 6 -1 2 4 1 -6 5}
3
6 6 4 4 4 5
那’关于Java中最大滑动窗口的全部。