文章目录


前言

哈喽,我是长路,目前刚刚大三,方向是后端也偶尔捣鼓下前端,现在的主语言是Java。之前一大段时间都是在学习web开发的一些技术,就很久没有进行类似于数据结构、算法之类的学习与刷题,打算这段时间拾起来好好学一学、搞一搞。

这段时间也是机缘巧合看到草帽路飞的博客,加了自学群,正巧看到博主组织在群里组织了leetcode刷题打卡活动,我也就参与进来,为期一个月,打算坚持每天都花一些时间做一些题目,并通过博客的方式来进行记录。

目前跟着一个Github仓库刷题(leetcode):代码随想录leetcode刷题,当前为栈与队列专题。




题目

题目来源leetcode

leetcode地址:​​239. 滑动窗口最大值​​,难度:困难。

题目描述(摘自leetcode):

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。

示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

示例 2:
输入:nums = [1], k = 1
输出:[1]

示例 3:
输入:nums = [1,-1], k = 1
输出:[1,-1]

示例 4:
输入:nums = [9,11], k = 2
输出:[11]

示例 5:
输入:nums = [4,-2], k = 2
输出:[4]

提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length

本地调试代码:

class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
...
}

public static void main(String[] args) {
System.out.println(Arrays.toString(new Solution().maxSlidingWindow(new int[]{1,3,-1,-3,5,3,6,7},3)));
}
}




题解

NO1:自定义队列

思路: 自定义一种队列规则,将入队元素依次从队列后往前比对,若是有小于的直接出队直到碰到大于的时候停止,接着再入队,此时队列队头一直会是最大值。

示例:1,3,-1,-3,5,3,6,7  k=3
队列 deque=[],左边是队头 下面=>指的是拿到队头的元素值,并没有直接出队
① [1,3,-1],-3,5,3,6,7 首先先将第一组的元素入队,最终队列为[3] => 3
② 1,[3,-1,-3],5,3,6,7 将1出队(队头若是1就将1出队,实际无),接着入队-3,-3与队列从后往前比,最终队列为[3,-3] => 3
③ 1,3,[-1,-3,5],3,6,7 将3出队(队头若是3就将3出队。实际有),接着入队5,5与队列往后往前比,最终队列为[5] => 5
④ 1,3,-1,[-3,5,3],6,7 将-1出队(队头若是-1就将-1出队。实际无),接着入队3,3与队列往后往前比,最终队列为[5,3] => 5
⑤ 1,3,-1,-3,[5,3,6],7 将6出队(队头若是-3就将-3出队。实际无),接着入队6,6与队列往后往前比,最终队列为[6] => 6
⑥ 1,3,-1,-3,5,[3,6,7] 将5出队(队头若是5就将5出队。实际无),接着入队7,7与队列往后往前比,最终队列为[7] => 7
最终每组值为[3,3,5,5,6,7]

代码

class MyQueue{
private Deque<Integer> deque;

public MyQueue(){
deque = new LinkedList<>();
}

//添加元素时,将队列从后往前比num小的出列
public void add(int num){
while(!deque.isEmpty() && deque.getLast() < num){
deque.pollLast();
}
deque.addLast(num);
}

//队头元素若是与num相同则出队
public void pop(int num){
if(!deque.isEmpty() && num == deque.getFirst()){
deque.pollFirst();
}
}

//获取队头元素(最大的值)
public int peek(){
return deque.getFirst();
}

}

class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
MyQueue myQueue = new MyQueue();
int[] maxNums = new int[nums.length-k+1];
//先将第一组的值放入,并且获取最大的值
for (int i = 0; i < k; i++) {
myQueue.add(nums[i]);
}
maxNums[0] = myQueue.peek();
for (int i = 1; i < maxNums.length; i++) {
//进来先去除不在范围内的元素
myQueue.pop(nums[i-1]);
//添加新的元素
myQueue.add(nums[i+k-1]);
//获取出当前队列中最大的元素
maxNums[i] = myQueue.peek();
}
return maxNums;
}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LqPo2Auu-1636733226055)(C:\Users\93997\AppData\Roaming\Typora\typora-user-images\image-20211103200737819.png)]




NO2:窗口内比较取值

思路:不使用某种数据结构,使用max、pos来临时存储最大值,max为最大值,pos为最大值索引。首先进行一轮比较得出对应的最大值,接着通过判断pos是否在下一次滑动窗口范围来进行相应的比较操作。

  • 这里有一点不太清楚的是仅仅通过滑动窗口最左、最优右临界值来与最大值进行比较就能够让时间效率优化那么多?若是k为很大情况,那么临界最左-最右中间这些还是要一个一个进行比较,难道没有消耗特别多时间吗?(该题解与我最初思路一致,只不过没有想到的是多增加了下面第15行临界最左值判断就能优化时间效率那么多)

代码

public int[] maxSlidingWindow(int[] nums, int k) {
int[] maxNums = new int[nums.length - k + 1];
int pos = -1;
int max = Integer.MIN_VALUE;
for (int i = 0,winEndPos = k-1; i < maxNums.length; i++,winEndPos++) {
if(pos >= i){
if(nums[winEndPos] >= max){
max = nums[winEndPos];
pos = winEndPos;
}
//下面nums[winEndPos]、nums[i]相当于窗口的最右侧与最左侧
}else if(nums[winEndPos] >= max-1){ //相当于>max,这里>=max-1,实际上是为了处理max = Integer.MIN_VALUE情况,-1由于超过原始长度就为最大值
max = nums[winEndPos];
pos = winEndPos;
}else if(nums[i] >= max-1){
max = nums[i];
pos = i;
}else{
//若是上面两个情况不过,那么就需要进行遍历重新比出最大值
max = nums[i];
for (int j = i+1; j < i + k; j++) {
if (nums[j] > max) {
max = nums[j];
pos = j;
}
}
}
maxNums[i] = max;
}
return maxNums;
}

leetcode【栈与队列—困难】 239.滑动窗口_职场和发展




参考文章

[1]. ​​leetcode题解​

[2]. ​​ 代码随想录— 239. 滑动窗口最大值​



我是长路,感谢你的耐心阅读。如有问题请指出,我会积极采纳!
欢迎关注我的公众号【长路Java】,分享Java学习文章及相关资料
Q群:851968786 我们可以一起探讨学习