2019.3.3 《剑指Offer》从零单刷个人笔记整理(66题全)目录传送门
这道题也是一道效率题,字符串和数组一般可以考虑在线处理。下面对算法过程进行说明:
1.创建一个用于记录元素下标的双端队列list和最终结果序列result。
以下对于测试用例:输入数组{2,3,4,2,6,2,5,1}、滑动窗口大小3,进行算法过程说明。
2.对list进行初始化,从初始窗口倒数第二个元素k开始执行一次操作:从前往后将队列中元素k小的所有元素删除,并将元素k的位置加在队首(记住这次操作,接下来还会用到)。
初始窗口中的元素为{2,3,4},对k=3之前的所有元素进行操作,由于3最大,对应下标为1,最终list={1}。
3.从第一个窗口(第一个窗口最后一个元素)开始,执行上述操作。
第一个窗口中的元素为{2,3,4},对k=4之前的所有元素进行操作,由于4最大,对应下标为1,最终list={2}。
4.对list队尾的最大元素,检查是否超出窗口范围(队首-队尾+1?窗口大小,只需检查一次),若超出则删去队尾元素。将最大元素加入result。
当窗口进行到{3,4,2}的时候,list中的元素为{3,2}(队首为3,队尾最大为2),检查一下3-2+1<3,符合要求,将位置2对应元素4加入result。
当窗口进行到{2,5,1}的时候,list中的元素为{7,6,4}(队首为7,由于6(对应数字5)不比7(对应数字1)小,因此不删除,队尾最大为4(对应数字6)),检查一下队尾7-4+1>3,说明数字6在窗口范围外,删去。list最终为{7,6},将位置6对应元素5加入result。
5.循环上述操作3与检查4的过程,可以保证每次list队尾元素最大。
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
Java实现:
/**
*
* @author ChopinXBP
* 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。
* 例如,如果输入数组{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]}。
*
*/
import java.util.ArrayList;
import java.util.LinkedList;
public class maxInWindows_63 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] num = {2,3,4,2,6,2,5,1};
ArrayList result = maxInWindows(num, 3);
for(int i = 0; i < result.size(); i++) {
System.out.print(result.get(i) + " ");
}
}
public static ArrayList maxInWindows(int [] num, int size)
{
ArrayList result = new ArrayList<>();
if(num == null || size <= 0 || size > num.length) return result;
//记录元素下标的队列 (只记录下标)
LinkedList list = new LinkedList<>();
//构建初始队列,从初始窗口倒数第二个元素k开始执行一次操作:从前往后将队列中元素k小的所有元素删除,并将元素k加在队首,此时队尾元素最大
for(int i = 0; i < size - 1; i++) {
while(!list.isEmpty() && num[i] > num[list.peekFirst()]) {
list.pollFirst();
}
list.addFirst(i);
}
//初始队列最大元素为size - 1个,对与每一个新元素k = num[i]
for(int i = size - 1; i < num.length; i++) {
//从前往后移去比k小的元素,将k插入队首
while(!list.isEmpty() && num[i] > num[list.peekFirst()]) {
list.pollFirst();
}
list.addFirst(i);
//检查队尾元素(当前最大值的坐标)是否超出窗口范围(被移出窗口),若超出则删去(每次移动最多删去一个)
if(i - list.peekLast() + 1 > size) {
list.pollLast();
}
result.add(num[list.peekLast()]);
}
return result;
}
}
C++实现示例:
class Solution {
public:
vector maxInWindows(const vector& num, unsigned int size)
{
vector res;
deque s;
for(unsigned int i=0;isize)
s.pop_front();
//把每次滑动的num下标加入队列
s.push_back(i);
//当滑动窗口首地址i大于等于size时才开始写入窗口最大值
if(size&&i+1>=size)
res.push_back(num[s.front()]);
}
return res;
}
};
#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#