数据结构:窗口最大值

最容易想到的算法是,遍历每个元素,还是在每个元素和后面两个元素进行比较获取最大值,这样一种两级循环的方式;没有问题。但是这样空间的复杂度就不是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 }

 

 

posted on 2018-01-28 22:48  下士闻道  阅读(463)  评论(0编辑  收藏  举报

导航