剑指Offer系列-面试题65:滑动窗口的最大值

题目:给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

思路:用一个双向链表存放数组的下标,让链表首的下标始终是当前窗口最大值的下标。下面以例子中的数组{2,3,4,2,6,2,5,1}解析一遍:

窗口大小为3,从数组第一个数字开始:

······当前下标为0,数字为2,当前链表为空,那么2一定是目前来说最大的值,将下标0放入链表,窗口还没有满,继续;

······当前下标为1,数字为3,当前链表首是0,下标0还在当前窗口中,不需要删除;当前链表尾部为0,下标为0的数小于当前数字,把链表尾部删除,链表为空,把当前下标1放入,窗口还没有满,继续

······当前下标为2,数字为4,当前链表首是1,下标1还在当前窗口中,不需要删除;当前链表尾部为1,下标为1的数小于当前数字,把链表尾删除,链表为空,把当前下标2放入,窗口已满(下标2其实是第三个数字,窗口大小为3,那么以后都不需要判断窗口是否满了),将当前链表首的下标所在数字放入结果集中(因为我们保持了链表首的下标是当前窗口最大值所在的下标);

······当前下标为3,数字为2,当前链表首是2,下标2还在当前窗口中,不需要删除;当前链表尾为2,下标为2的数大于当前数字,但是下标为2的数字可能随着窗口移动而被抛弃,当前数字还是可能成为窗口中的最大值的,那么先把当前下标放入,此时链表中有下标2和3;将当前链表首的下标所在数字放入结果集中;

······当前下标为4,数字为6,当前链表首是2,下标2还在当前窗口中,不需要删除;当前链表尾为3,下标为3的数小于当前数字,把链表尾删除,链表中链表尾此时的下标是2,而下标2的数字依旧小于当前下标的数字6,继续删除链表尾,此时链表为空,把当前下标4放入,将当前链表首的下标所在数字放入结果集中;

······当前下标为5,数字为2,当前链表首是4,下标4还在当前窗口中,不需要删除;当前链表尾为4,下标为4的数大于当前数字,但是下标为4的数字可能随着窗口移动而被抛弃,当前数字还是可能成为窗口中的最大值的,把当前下标5放入,将当前链表首的下标所在数字放入结果集中;

······

       经过一番推论,我们可以得到具体的思路,首先我们需要循环判断(用while进行判断)当前链表头部的下标是不是还在窗口中,如果不在就删除它;然后循环判断(用while进行判断)当前链表尾的下标位置的数字是否小于当前数字如果小于,那么这个下标所在的数字一定不是当前窗口的最大值,以后也不会是,那么删除,再次判断;如果当前链表尾的下标位置的数字大于当前数字,那么当前数字还是可能成为最大值的,因为前面的数字总要移出窗口的,把当前下标放入链表尾部;此时链表头部是当前窗口的最大值所在的下标,放入结果集中,继续遍历。

       根据上面的思路,我们可以写出下面的代码:

代码:

public ArrayList<Integer> maxInWindows(int [] num, int size) {
    ArrayList<Integer> list = new ArrayList<Integer>();
    // 输入合法性判断
    if (num == null || num.length < 1 || size < 1) {
    	return list;
    }
    // 双向链表,链表中存放的是下标,链表首存放的是当前窗口最大值所在的下标
    LinkedList<Integer> queue = new LinkedList<Integer>();
    for (int i = 0 ; i < num.length ; i++) {
    	// 链表尾部存放的值小于当前值,就去除
    	while (queue.size() > 0 && num[queue.getLast()] <= num[i]) {
    		queue.removeLast();
    	}
    	// 如果链表首的下标已经不在滑动窗口内,那么删除它
    	while (queue.size() > 0 && i - queue.getFirst() > size - 1) {
    		queue.removeFirst();
    	}
    	queue.add(i);
    	// 滑动窗口已满,放入最大的值,即链表首
    	if (i >= size - 1) {
    		list.add(num[queue.getFirst()]);
    	}
    }
    return list;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值