zoukankan      html  css  js  c++  java
  • 数据结构:窗口最大值

    最容易想到的算法是,遍历每个元素,还是在每个元素和后面两个元素进行比较获取最大值,这样一种两级循环的方式;没有问题。但是这样空间的复杂度就不是O(N)了,而是O(N*WindowCount)。
    实现O(N)的一种方式就是:利用双端列表来实现。
    一个源,数组,从入口参数中传入,一个变量,窗口Count;
    从源中取出一个数据,判断当前列表是否为空,为空直接让如;如果不为空,则从队尾中取出元素进行比较。如果说当前元素比队尾元素大,则队尾元素出队列,再取出新的队尾元素和当前元素比较,直到发现了一个队尾元素比当前元素大或者队列为空。
    注意,数据结构通常的思路:考虑清楚退出条件;退出条件主要有两种:递归(不断地出栈、出队)的判断,设置条件,一个业务谓词(逻辑判断,大,小,数量达到某个值);第二种就是 结构谓词(队列为空)。
    另外上面描述的逻辑中有一个常见的算法概念:空降高权。什么叫空降最大,就是待处理的元素就是空降元素,高权就是无论该空降元素是大是小,一定是要放入到队列中的;因为如果它小,那么它在未来是可能成为最大的,你要留着它,如果它大,那么就要把队列中现有,从队尾倒撸,没它大的都干掉,然后在放入到队尾。
    只要处理的次数达到了windowsCount,就需要每次都从队头中取出元素,记做当前窗口的最大值(只要第一次处理次数达到了窗口数,以后每次处理一个都意味着一个窗口满了)
    这里还有一个判断,就是如果一个元素很大,但是他不能一直在队头,如果窗口已经划过就不能在队头呆着,可以说是过期了,怎么判断?就看每次放入一个数据后,都check一下队列的数量,达到了窗口数量,就意味着队头元素已经过期(新来的元素一定是要挤掉这个元素的),于是将该队头元素删除。
    实现上,队列(双端)采用的是LinkedList,之前采用ArrayList,为了实现循环删除队队尾,我需要单独写一个函数,因为java里面的ArrayList是不能直接遍历删除元素,需要转化为Iterator,然后用这个Iterator来动态(遍历)删除。
    另外,通过这个例子,我觉得做这类问题,有一点非常重要:就是有一种建立模型的意识,或者讲是“翻译”的意识,比如窗口最大值,其实翻译过来就是:每三个值中选出最大的;注意只关心最大值,到此算是翻译到家了;再分析数据,数据分为两种,一种是特殊数据,首先就是这个最大值,在数据接结构里面,最大值就是要放在特殊位置,队头或者队尾,以便于取出;另外还需要一个数组来存放每次被识别出来的最大值;另外一种其他数据,从要求来看,其他数据无所谓,直接出队列即可;最后梳理数据的生命周期,从特殊数据分析,从放入到超时,超时处理为从队列弹出(弹出上面没有分析到,在这个环节被分析出来);对于普通数据,从放入,到比较后发现小,弹出完事。
    数据结构分析到此结束,下面就是考虑技术实现了。比如在此例中,使用LinkedList最为合适,因为是队列形式,还提供了队头队尾的poll;
    源码:
     1 public static void main(String[] args) {
     2         int[] values = { 3, 2, 8, 9, 9, 0, 2, 1, 5, 5, 1 };
     3         WindowMaxValueQueue queue = new WindowMaxValueQueue();
     4         queue.run(values, 3);
     5     }
     6 
     7     public void run(int[] values, int windowCount) {
     8         int counter = 0;
     9         LinkedList<Integer> lst = new LinkedList<Integer>();
    10         List<Integer> maxValues = new ArrayList<Integer>();
    11         for (int value : values) {
    12             logger.info("待处理元素: {}", value);
    13             // 队列中没有元素直接放入list中
    14             if (lst.size() == 0) {
    15                 logger.info("队列为空,直接放入元素: {}", value);
    16                 lst.add(value);
    17             } else {
    18                 while (lst.size() > 0) {
    19                     int tail = lst.peekLast();
    20                     if (tail <= value) {
    21                         lst.removeLast();
    22                     } else {
    23                         break;
    24                     }
    25                 }
    26                 lst.add(value);
    27 
    28                 String elements = "";
    29                 for (int i = 0; i < lst.size(); i++) {
    30                     elements = elements + lst.get(i) + "; ";
    31                 }
    32 
    33                 logger.info("添加元素: {}; 队列元素列表: {}", value, elements);
    34             }
    35 
    36             counter++;
    37             // 只要索引大于了窗口,每个加入的元素都会产生一个窗口最大值
    38             if (counter >= windowCount) {
    39                 int head = lst.get(0);
    40                 logger.info("窗口最大值: {}", head);
    41                 maxValues.add(head);
    42             }
    43             // 如果队列个数满足了三个,头元素删除
    44             if (lst.size() == 3) {
    45                 logger.info("窗口数量达到了三个,头元素: {}进行删除", lst.get(0));
    46                 lst.remove(0);
    47             }
    48         }
    49 
    50         int index = 1;
    51         for (int maxValue : maxValues) {
    52             System.out.println(index++ + ". value: " + maxValue);
    53         }
    54     }
    55 }
     
  • 相关阅读:
    苹果开发者账号证书、授权配置文件设置流程
    JQuery 总结(1) 选择器的使用
    PC和移动端自动识别
    前端获取ip的接口
    AJAX 跨域解决办法
    linux
    复制剪切板实现
    css animation 制作打开动画效果
    弹出层播放视频
    盛世龙图项目总结
  • 原文地址:https://www.cnblogs.com/xiashiwendao/p/8372769.html
Copyright © 2011-2022 走看看