剑指Offer-59: 滑动窗口的最大值 和 队列的最大值

题目

给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。例如,如果输入数组{2, 3, 4, 2, 6, 2, 5, 1}及滑动窗口的大小3,那么一共存在6个滑动窗口,它们的最大值分别为{4, 4, 6, 6, 6, 5}

思路

法一:简单的暴力法
法二:双向队列.用一个双向队列,队列第一个位置保存当前窗口的最大值,当窗口滑动一次,判断当前最大值是否过期(当前最大值的位置是不是在窗口之外),新增加的值从队尾开始比较,把所有比他小的值丢掉。这样时间复杂度为O(n)。

蛮力直接在每个滑动窗口依次比较找出最大值,时间复杂度太高。

我们考虑把每个可能成为最大值的数字记录下来,就可以快速的得到最大值。

思路:

建立一个两端开口的队列,放置所有可能是最大值的数字(存放的其实是对应的下标),且最大值位于队列开头。从头开始扫描数组,

如果遇到的数字比队列中所有的数字都大,那么它就是最大值,其它数字不可能是最大值了,将队列中的所有数字清空,放入该数字,该数字位于队列头部;

如果遇到的数字比队列中的所有数字都小,那么它还有可能成为之后滑动窗口的最大值,放入队列的末尾;

如果遇到的数字比队列中最大值小,最小值大,那么将比它小数字不可能成为最大值了,删除较小的数字,放入该数字。

由于滑动窗口有大小,因此,队列头部的数字如果其下标离滑动窗口末尾的距离大于窗口大小,那么也删除队列头部的数字。

注:队列中存放的是下标,以上讲的 队列头部的数字 均指 队列头部的下标所指向的数字。写代码时不要弄混了

//思路:在保留了最大值的同时,需要保留窗口内该最大值右边的次大值(也有可能比最大值还大,此时该点成为最大值)。
//当最大值移出窗口时,次大值顶替最大值。
    public ArrayList<Integer> maxInWindows(int [] num, int size) {
    	ArrayList<Integer> ret = new ArrayList<>();
        if(num.length == 0 || num.length < size || size<1)
        	return ret;
        if(size == 1) {
            for(int i=0; i<num.length; i++)
            	ret.add(num[i]);
            return ret;
        }
        //1.寻找windows内最大值
        int max = num[0];
        int max_index = 0;
        int ci_max_index = 0;
        boolean have_ci = false;
        for(int i=1; i<size; i++) {
        	if(num[i] > max) {
        		max = num[i];
        		max_index = i;
        	}else {
        		if(!have_ci) {
        			ci_max_index = i;
        			have_ci = true;
        		}else if(num[i] >= num[ci_max_index]) {
        			ci_max_index = i;
        		}
        	}
        }
        if(ci_max_index <= max_index) {
        	have_ci = false;
        }
        ret.add(max);
        for(int i=size; i<num.length; i++) {
        	//老大要退休
        	if(i-size >= max_index) {
        		//如果有老二
        		if(have_ci) {
        			//看老二和新人哪个当老大
        			//老二当老大
        			if(num[ci_max_index]>num[i]) {
        				max_index = ci_max_index;
        				max = num[max_index];
        				//最大的走了,次大的当上了老大,这时候发现没有当次大的了,
        				//因为次大和扫描到的该点之前有可能存在比该扫描到的点大的,而之前没有保存。故应该保存三个点。
        				//对于size == 2的需要特殊注意下。此处不想改动了,故使用遍历的方式实现。
        				ci_max_index = i;
        				int third_max = num[max_index+1];
        				for(int k=max_index+1; k<=i; k++) {
        					if(num[k] >= third_max) {
        						ci_max_index = k;
        						third_max = num[ci_max_index];
        					}
        				}
        				have_ci = true;
        			}else {  //新人当老大
        				max_index = i;
        				max = num[max_index];
        				have_ci = false;
        			}
        			
        		//没有老二
        		}else {  //如果没有老二 ?应该不存在这种情况
        			max_index = i;
        			max = num[max_index];
        			have_ci = false;
        		}
        		ret.add(max);
        		continue;
        	}
        	//老大不退休
        	//新人当老大
        	if(num[i] >= max) {
        		max_index = i;
        		max = num[max_index];
        		have_ci = false;
        	}else {  //老大还是老大
        		//如果没老二 ?应该不存在这种情况
        		if(!have_ci) {
        			ci_max_index = i;
        			have_ci = true;
        		}else {   //如果有老二,看看谁当老二
        			//新人当老二
        			if(num[i] >= num[ci_max_index]) {
        				ci_max_index = i;
        				have_ci = true;
        			}
        		}
        	}
        	ret.add(max);
        }
        return ret;
 }

法二:

import java.util.ArrayList;
import java.util.LinkedList;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size){
        ArrayList<Integer> res = new ArrayList<Integer>();
        LinkedList<Integer> deque = new LinkedList<Integer>();
        if(num.length == 0 || size == 0)
            return res;
        for(int i = 0; i < num.length; i++){
            if(!deque.isEmpty() && deque.peekFirst() <= i - size)
                deque.poll();
            while(!deque.isEmpty() && num[deque.peekLast()] < num[i])
                deque.removeLast();
            deque.offerLast(i);
            if(i + 1 >= size)
                res.add(num[deque.peekFirst()]);
        }
        return res;
    }
}

题目

请定义一个队列并实现函数max得到队列里的最大值,要求函数max、push_back和pop_front的时间复杂度都是O(1)。

思路

与滑动窗口的最大值一题相似,利用一个双端队列来存储当前队列里的最大值以及之后可能的最大值。

在定义题目要求功能的队列时,除了定义一个队列data存储数值,还需额外用一个队列maxmium存储可能的最大值;此外,还要定义一个数据结构,用于存放数据以及当前的index值,用于删除操作时确定是否删除maxmium中最大值。

public class P292_QueueWithMax {
    public static class QueueWithMax<T extends Comparable> {
        private Deque<InternalData<T>> queueData;
        private Deque<InternalData<T>> queueMax;
        private int currentIndex;
        public QueueWithMax() {
            this.queueData = new ArrayDeque<>();
            this.queueMax = new ArrayDeque<>();
            this.currentIndex = 0;
        }
        public T max(){
            if(queueMax.isEmpty())
                return null;
            return queueMax.getFirst().value;
        }
        public void pushBack(T value){
            while (!queueMax.isEmpty()&&value.compareTo(queueMax.getLast().value)>=0)
                queueMax.removeLast();
            InternalData<T> addData = new InternalData<>(value,currentIndex);
            queueMax.addLast(addData);
            queueData.addLast(addData);
            currentIndex++;
        }
        public T popFront(){
            if(queueData.isEmpty())
                return null;
            InternalData<T> delData = queueData.removeFirst();
            if(delData.index==queueMax.getFirst().index)
                queueMax.removeFirst();
            return delData.value;
        }
        private static class InternalData<M extends Comparable> {
            public M value;
            public int index;
            public InternalData(M value,int index){
                this.value = value;
                this.index = index;
            }
        }
    }
    public static void main(String[] args) {
        QueueWithMax<Integer> queue = new QueueWithMax<>();
        queue.pushBack(3);
        System.out.println(queue.max());
        queue.pushBack(5);
        System.out.println(queue.max());
        queue.pushBack(1);
        System.out.println(queue.max());
        System.out.println("开始出队后,调用max");
        System.out.println(queue.max());
        queue.popFront();
        System.out.println(queue.max());
        queue.popFront();
        System.out.println(queue.max());
        queue.popFront();
        System.out.println(queue.max());
    }
}

猜你喜欢

转载自blog.csdn.net/qq_32534441/article/details/89241553