题目:给定一个数组和滑动窗体的大小,请找出全部滑动窗体里的最大值。


举例说明

  比如,假设输入数组{2,3,4,2,6,2,5,1}及滑动窗体的大小。那么一共存在6个滑动窗体,它们的最大值分别为{4,4,6,6,6,5}。

解题思路

  假设採用蛮力法,这个问题似乎不难解决:能够扫描每个滑动窗体的全部数字并找出当中的最大值。假设滑动窗体的大小为k,须要O(k)时间才干找出滑动窗体里的最大值。对于长度为n的输入数组,这个算法总的时间复杂度是O(nk)。

  实际上一个滑动窗体能够看成是一个队列。当窗体滑动时,处于窗体的第一个数字被删除,同一时候在窗体的末尾加入一个新的数字。

这符合队列的先进先出特性。

假设能从队列中找出它的最大数,这个问题也就攻克了。

  在面试题21中。我们实现了一个能够用O(1)时间得到最小值的栈。相同,也能够用O(1)时间得到栈的最大值。同一时候在面试题7中,我们讨论了怎样用两个栈实现一个队列。

综合这两个问题的解决方法,我们发现假设把队列用两个栈实现,因为能够用O(1)时间得到栈中的最大值。那么也就能够用O(1)时间得到队列的最大值,因此总的时间复杂度也就降到了O(n)。

  我们能够用这种方法来解决这个问题。

只是这样就相当于在一轮面试的时间内要做两个面试题,时间未必够用。再来看看有没有其他的方法。

  以下换一种思路。我们并不把滑动窗体的每个数值都存入队列中,而仅仅把有可能成为滑动窗体最大值的数值存入到一个两端开口的队列。接着以输入数字{2,3,4,2,6,2,5,1}为例一步分析。

  数组的第一个数字是2,把它存入队列中。第二个数字是3.因为它比前一个数字2大,因此2不可能成为滑动窗体中的最大值。

2先从队列里删除,再把3存入到队列中。此时队列中仅仅有一个数字3.针对第三个数字4的步骤相似,终于在队列中仅仅剩下一个数字4.此时滑动窗体中已经有3个数字。而它的最大值4位于队列的头部。

  接下来处理第四个数字2。2比队列中的数字4小。当4滑出窗体之后2还是有可能成为滑动窗体的最大值,因此把2存入队列的尾部。

如今队列中有两个数字4和2,当中最大值4仍然位于队列的头部。

  下一个数字是6.因为它比队列中已有的数字4和2都大,因此这时4和2已经不可能成为滑动窗体中的最大值。先把4和2从队列中删除。再把数字6存入队列。这个时候最大值6仍然位于队列的头部。

  第六个数字是2.因为它比队列中已有的数字6小,所以2也存入队列的尾部。此时队列中有两个数字,当中最大值6位于队列的头部。

  接下来的数字是5.在队列中已有的两个数字6和2里。2小于5。因此2不可能是一个滑动窗体的最大值,能够把它从队列的尾部删除。删除数字2之后,再把数字5存入队列。此时队列里剩下两个数字6和5,当中位于队列头部的是最大值6.

  数组最后一个数字是1。把1存入队列的尾部。注意到位于队列头部的数字6是数组的第5个数字,此时的滑动窗体已经不包含这个数字了,因此应该把数字6从队列删除。那么怎么知道滑动窗体是否包含一个数字?应该在队列里存入数字在数组里的下标,而不是数值。当一个数字的下标与当前处理的数字的下标之差大于或者等于滑动窗体的大小时。这个数字已经从滑动窗体中滑出,能够从队列中删除了。

代码实现

import java.util.*;

public class Test65 {
private static List<Integer> maxInWindows(List<Integer> data, int size) {
List<Integer> windowMax = new LinkedList<>(); // 条件检查
if (data == null || size < 1 || data.size() < 1) {
return windowMax;
} Deque<Integer> idx = new LinkedList<>(); // 窗体还没有被填满时。找最大值的索引
for (int i = 0; i < size && i < data.size(); i++) {
// 假设索引相应的值比之前存储的索引值相应的值大或者相等,就删除之前存储的值
while (!idx.isEmpty() && data.get(i) >= data.get(idx.getLast())) {
idx.removeLast();
} // 加入索引
idx.addLast(i);
} // 窗体已经被填满了
for (int i = size; i < data.size(); i++) {
// 第一个窗体的最大值保存
windowMax.add(data.get(idx.getFirst())); // 假设索引相应的值比之前存储的索引值相应的值大或者相等。就删除之前存储的值
while (!idx.isEmpty() && data.get(i) >= data.get(idx.getLast())) {
idx.removeLast();
} // 删除已经滑出窗体的数据相应的下标
if (!idx.isEmpty() && idx.getFirst() <= (i - size)) {
idx.removeFirst();
} // 可能的最大的下标索引入队
idx.addLast(i);
} // 最后一个窗体最大值入队
windowMax.add(data.get(idx.getFirst())); return windowMax; } private static List<Integer> arrayToCollection(int[] array) {
List<Integer> result = new LinkedList<>();
if (array != null) {
for (int i : array) {
result.add(i);
}
} return result;
} public static void main(String[] args) { // expected {7};
List<Integer> data1 = arrayToCollection(new int[]{1, 3, -1, -3, 5, 3, 6, 7});
System.out.println(data1 + "," + maxInWindows(data1, 10)); // expected {3, 3, 5, 5, 6, 7};
List<Integer> data2 = arrayToCollection(new int[]{1, 3, -1, -3, 5, 3, 6, 7});
System.out.println(data2 + "," + maxInWindows(data2, 3)); // expected {7, 9, 11, 13, 15};
List<Integer> data3 = arrayToCollection(new int[]{1, 3, 5, 7, 9, 11, 13, 15});
System.out.println(data3 + "," + maxInWindows(data3, 4)); // expected {16, 14, 12};
List<Integer> data5 = arrayToCollection(new int[]{16, 14, 12, 10, 8, 6, 4});
System.out.println(data5 + "," + maxInWindows(data5, 5)); // expected {10, 14, 12, 11};
List<Integer> data6 = arrayToCollection(new int[]{10, 14, 12, 11});
System.out.println(data6 + "," + maxInWindows(data6, 1)); // expected {14};
List<Integer> data7 = arrayToCollection(new int[]{10, 14, 12, 11});
System.out.println(data7 + "," + maxInWindows(data7, 4));
}
}

执行结果

特别说明

欢迎转载,转载请注明出处【http://blog.csdn.net/DERRANTCM/article/details/46872411

【剑指Offer学习】【面试题65:滑动窗体的最大值】的更多相关文章

  1. 【剑指offer】面试题 65. 不用加减乘除做加法

    面试题 65. 不用加减乘除做加法 题目描述 题目:写一个函数,求两个整数之和,要求在函数体内不得使用+.-.*./四则运算符号. Java 实现 public class Solution {    ...

  2. 《剑指offer》面试题65. 不用加减乘除做加法

    问题描述 写一个函数,求两个整数之和,要求在函数体内不得使用 "+"."-"."*"."/" 四则运算符号. 示例: 输 ...

  3. 《剑指offer》面试题59 - II. 队列的最大值

    问题描述 请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value.push_back 和 pop_front 的均摊时间复杂度都是O(1). 若队列为空,pop_ ...

  4. 【剑指Offer学习】【全部面试题汇总】

    剑指Offer学习 剑指Offer这本书已经学习完了.从中也学习到了不少的东西,如今做一个总的文件夹.供自已和大家一起參考.学如逆水行舟.不进则退.仅仅有不断地学习才干跟上时候.跟得上技术的潮流! 全 ...

  5. 【剑指Offer学习】【所有面试题汇总】

    剑指Offer学习 剑指Offer这本书已经学习完了,从中也学习到了不少的东西,现在做一个总的目录,供自已和大家一起参考,学如逆水行舟,不进则退.只有不断地学习才能跟上时候,跟得上技术的潮流! 所有代 ...

  6. 剑指Offer——滴滴笔试题+知识点总结

    剑指Offer--滴滴笔试题+知识点总结 情景回顾 时间:2016.9.18 15:00-17:00 地点:山东省网络环境智能计算技术重点实验室 事件:滴滴笔试   总体来说,滴滴笔试内容体量不算多, ...

  7. 剑指Offer——网易笔试题+知识点总结

    剑指Offer--网易笔试题+知识点总结 Fibonacci package cn.edu.ujn.nk; import java.util.ArrayList; import java.util.S ...

  8. 剑指Offer——咪咕笔试题+知识点总结

    剑指Offer--咪咕笔试题+知识点总结 情景回顾 时间:2016.10.09 15:00-16:30 地点:山东省网络环境智能计算技术重点实验室 事件:咪咕笔试 知识点总结 1.Html设置格式贵阳 ...

  9. 剑指Offer——迅雷笔试题+知识点总结

    剑指Offer--迅雷笔试题+知识点总结 情景回顾 时间:2016.9.19 19:00-21:00 地点:山东省网络环境智能计算技术重点实验室 事件:迅雷笔试 总体来说,迅雷笔试内容体量不算多,主要 ...

  10. 《剑指offer》面试题39 二叉树的深度(java)

    摘要: 今天翻到了<剑指offer>面试题39,题目二中的解法二是在函数的参数列表中通过指针的方式进行传值,而java是没有指针的,所以函数要进行改造.然而我翻了下别人的java版本(我就 ...

随机推荐

  1. 使用GetLogicalDrives获取卷标

    #include<stdio.h> #include<windows.h> int main() { DWORD dwLogical= GetLogicalDrives(); ...

  2. windows sdk编程隐藏窗体标题栏

    #include <windows.h> /*消息处理函数声明*/ HRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM ...

  3. Dynamic Web Module版本对应tomcat版本

    MyEclipse2017+JDK 1.8环境下 Dynamic Web Module版本3.1要对应tomcat7.0以上版本,不然部署项目时会出现错误(会出现无法部署项目的情况). Dynamic ...

  4. JavaSE-13 内部类

    学习要点 内部类的定义 内部类的应用 内部类 定义 Java的一个类中包含着另一类. A类和B类是C类的外部类.B类是C类的外部类.A类也称为顶层类. 如何使用内部类 public class MyF ...

  5. gym100825G. Tray Bien(轮廓线DP)

    题意:3 * N的格子 有一些点是坏的 用1X1和1X2的砖铺有多少种方法 题解:重新学了下轮廓线 写的很舒服 #include <bits/stdc++.h> using namespa ...

  6. UIScrollView的contentSize、contentOffset和contentInset属性

    IOS中,UIScrollView是可以滚动的视图,其中最常用的UITableView就是继承了UIScrollView. 跟所有的view一样,UIScrollView有一个frame属性,同时,U ...

  7. 大项目之网上书城(七)——书页面以及加入购物车Servlet

    目录 大项目之网上书城(七)--书页面以及加入购物车Servlet 主要改动 1.shu.jsp 代码 效果图 2.shu.js 代码 3.index.jsp 代码 效果图 4.FindBookByC ...

  8. 虚拟机Linux与本地虚拟网卡配置---NAT链接方式

    虚拟机Linux与本地虚拟网卡配置---NAT链接方式 **********这是我亲自尝试多次实践出来的结果,不是复制粘贴************************* 首先进行初始化,这样避免有 ...

  9. vue开发--生成token并保存到本地存储中

    首先回顾一下token:token认证是RESTFUL.api的一个很重要的部分,通过token认证和token设置,后端会有一个接口传给前台: http://localhost/yiiserver/ ...

  10. buf.entries()详解

    buf.entries() 返回:{Iterator} 从当前 Buffer 的内容中,创建并返回一个 [index, byte] 形式的迭代器. const buf = Buffer.from('b ...