声名:问题描述来源于leetcode:
日记-lc题解
一、问题描述
239. 滑动窗口最大值
难度困难1904
给你一个整数数组 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]
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
二、题解
思路1:
最平常的!
暴力解:
题解1:
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums.length <= k){
return maxNums(nums);
}
Queue<Integer> result = new LinkedList<>();
List<Integer> list = new LinkedList<>();
for (int i = 0; i < nums.length; i++) {
if (list.size() < k){
list.add(nums[i]);
}else{// list.size() == k
result.add(maxLinkedList(list));
list.remove(0);
list.add(nums[i]);
}
}
result.add(maxLinkedList(list));
return QueueToArray(result);
}
private int[] maxNums(int[] nums) {
int maxNumber = nums[0];
for (int i : nums) {
if (maxNumber < i) maxNumber = i;
}
return new int[]{maxNumber};
}
private int[] QueueToArray(Queue<Integer> result) {
int size = result.size();
int[] resultArray = new int[size];
for (int i = 0; i < size; i++) {
resultArray[i] = result.poll();
}
return resultArray;
}
private Integer maxLinkedList(List<Integer> list) {
int maxNum = list.get(0);
for (int i : list) {
if (maxNum < i){
maxNum = i;
}
}
return maxNum;
}
}
结果:
再来:
题解2:
class MyQueue{
private LinkedList<Integer> list = new LinkedList<>();
public void addElement(Integer num){
list.add(num);
}
public void addElementAndSort(Integer num){
list.add(num);
list.sort((num1,num2)->{
return num1 - num2;
});
}
public Integer getMaxNum(Integer deleteNode){
Integer resultNum = list.get(list.size() - 1);
list.removeFirstOccurrence(deleteNode);
return resultNum;
}
public int size(){
return list.size();
}
public int maxNum(){
return list.get(list.size() - 1);
}
}
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (k == 1) return nums;
if (nums.length <= k) return maxNums(nums);
MyQueue myQueue = new MyQueue();
Queue<Integer> resultQueue = new LinkedList<>();
myQueue.addElement(nums[0]);
for (int index = 1; index < nums.length; index++) {
if (myQueue.size() == k){
resultQueue.add(myQueue.getMaxNum(nums[index - k]));
}
if (nums[index] >= myQueue.maxNum()){
myQueue.addElement(nums[index]);
}else {
myQueue.addElementAndSort(nums[index]);
}
}
resultQueue.add(myQueue.maxNum());
return resultArray(resultQueue);
}
private int[] resultArray(Queue<Integer> resultQueue) {
int size = resultQueue.size();
int[] resultNums = new int[size];
for (int i = 0; i < size; i++) {
resultNums[i] = resultQueue.poll();
}
return resultNums;
}
private int[] maxNums(int[] nums) {
int maxNum = nums[0];
for (int num : nums) {
if (maxNum < num) maxNum = num;
}
return new int[]{maxNum};
}
}
这个结果果然还是不行,关键是寻找将容器里的元素排序的算法优化,还是卡在第37个案例中。排序部分其实可以用二分排序来优化一下
题解3:
个人优化算法(排序部分)
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
public int[] maxSlidingWindow(int[] nums, int k) {
if (k == 1) return nums;//如果是1则直接返回
if (nums.length <= k) return maxNums(nums);
List<Integer> indexList = new LinkedList<>();
int maxIndex = -1;
for (int index = 0; index < nums.length; index++) {
if (index + k > nums.length) break;//如果到了边界值那么就已经到尽头了,直接结束
if (index <= maxIndex){//如果最大值索引在左索引右边,那么nums[maxIndex]除了和右边界的值没有确定,其他的均确定了
if (nums[maxIndex] <= nums[index + k - 1]){//只需要比较右边界的值
maxIndex = index + k -1;
}
indexList.add(maxIndex);
continue;
}
maxIndex = index;//此时的最大值索引是在[index,index+k)之间,所以要将maxIndex更新,才可以参与循环
for (int innerIndex = index; innerIndex < index + k; innerIndex++) {
if (nums[maxIndex] <= nums[innerIndex]) maxIndex = innerIndex;
}
indexList.add(maxIndex);
}
return maxArray(indexList,nums);
}
private int[] maxArray(List<Integer> indexList, int[] nums) {
int[] result = new int[indexList.size()];
int resultIndex = 0;
for (int index : indexList) result[resultIndex++] = nums[index];
return result;
}
private int[] maxNums(int[] nums) {
int maxNum = nums[0];
for (int num : nums) {
if (maxNum < num) maxNum = num;
}
return new int[]{maxNum};
}
}
运行:
又想到了还有一个思路:就是自定义一个容器对选取最大元素进行优化获取最大元素的时间,下面是该容器的属性:
- 容器最大可以装k个元素node
- 容器每个元素的属性有:order_by_insertTime、val、order_byVal
- 有序性:按照val的大小排序
- 对于相同val的元素,其排序也有细节要求;
- 容器有添加元素的方法add(node)
- 容器有删除元素的方法,是指删除添加时间最早的元素remove()
- 容器有获取最大val的元素的val的方法getMaxVal()
- 添加元素的方法是insert(node):将node添加到指定的位置,以保持有序性
- 删除前会调用remove方法
这样子就可以将k个元素放进去,获取最大的出来了,但是感觉还是复杂的没最优,但是还是去实现一下:
我们可以遍历一次nums数组,当添加到k个元素时,便调用下该容器的getMaxVal()方法,将获取到的val用队列queue保存;接着循环继续时,再容器添加一个元素node,那么就先调用容器的remove方法将最先添加的元素删除,再将新的元素insert(node)进来,接着便再将最大元素返回,queue.add(容器.getMaxVal());
当循环走到nums最后一个数值后那么结束循环;
接着将queue的值抽到数组里返回
但是这个思路还是有点难实现的,于是又搁置了。。。
题解4:
public class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (k == 1) return nums;
if (k >= nums.length) return maxNums(nums);
int[] resultNums = new int[nums.length - k + 1];
MyQueue myQueue = new MyQueue();
for (int i = 0; i < k; i++) {
myQueue.add(nums[i]);
}
int index = 0;
resultNums[index++] = myQueue.peek();
for (int i = k; i < nums.length; i++) {//这里面会为resultNums更新nums.lengh-k个元素
//删除元素,如果nums[i - k]是最大值时,那么肯定是要干掉这个元素的,
// 如果不是最大值,那么myQueue里根本不可能存在nums[i - k],因为都被add方法干掉了
myQueue.pop(nums[i - k]);
myQueue.add(nums[i]);
resultNums[index++] = myQueue.peek();
}
return resultNums;
}
private int[] maxNums(int[] nums) {
int maxNum = nums[0];
for (int num : nums) {
if (maxNum < num) maxNum = num;
}
return new int[]{maxNum};
}
}
class MyQueue{
LinkedList<Integer> list = new LinkedList<>();
//返回索引为0的元素值
int peek(){
return list.peek();
}
void add(int val){
//保证最前面的最大
while (!list.isEmpty() && val > list.getLast()){
list.removeLast();
}
list.add(val);
}
void pop(int val){
if (!list.isEmpty() && val == list.peek()) list.pop();
}
}
心得:
可以使用一个容器保存一系列节点,规定第一个节点的值最大,每次添加val的节点时需要删除一部分小值的节点,这么删除呢?是从元素列表的末尾向头部的方向遍历,一旦小于或等于val,那么就删除,直到遍历到比val更大的节点就退出删除操作,然后就挂在节点列表尾部。
另外,一开始就先得往容器处理前k个元素。然后才可以遍历nums[k]后面的元素,每次遍历都会对容器的首元素进行判断,判断是不是要删除的nums[index - k]元素。一开始xin麒会想到比如说有如[5,3,4,2,1,1,1,1,1,1,1,1],k = 5时,会不会删除到3这个元素呢?这个是不会发生的,因为它在添加操作时就已经被干掉了。在与nums[index - k]比较时,容器里根本就没有3这个元素。
注意:MyQueue的pop的参数类型是int不是包装类Integer,具体是什么原因呢?是lc官网的jdk版本不对还是什么呢?还是说在jdk8中有自动拆箱但是因为官网不是jdk8导致的呢?xin麒打算蹲一个有缘人解答,或者后面有时间再去敲打下背后的原因。。。
test:
public class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (k == 1) return nums;
if (k >= nums.length) return maxNums(nums);
int[] resultNums = new int[nums.length - k + 1];
MyQueue myQueue = new MyQueue();
for (int i = 0; i < k; i++) {
myQueue.add(nums[i]);
}
int index = 0;
resultNums[index++] = myQueue.peek();
for (int i = k; i < nums.length; i++) {//这里面会为resultNums更新nums.lengh-k个元素
//删除元素,如果nums[i - k]是最大值时,那么肯定是要干掉这个元素的,
// 如果不是最大值,那么myQueue里根本不可能存在nums[i - k],因为都被add方法干掉了
myQueue.pop(nums[i - k]);
myQueue.add(nums[i]);
resultNums[index++] = myQueue.peek();
}
return resultNums;
}
private int[] maxNums(int[] nums) {
int maxNum = nums[0];
for (int num : nums) {
if (maxNum < num) maxNum = num;
}
return new int[]{maxNum};
}
}
class MyQueue{
LinkedList<Integer> list = new LinkedList<>();
//返回索引为0的元素值
Integer peek(){
return list.peek();
}
void add(Integer val){
//保证最前面的最大
while (!list.isEmpty() && val > list.getLast()){
list.removeLast();
}
list.add(val);
}
void pop(int val){
if (!list.isEmpty() && val == list.peek()) list.pop();
}
}
上面的是可以通过
下面的不行:
public class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (k == 1) return nums;
if (k >= nums.length) return maxNums(nums);
int[] resultNums = new int[nums.length - k + 1];
MyQueue myQueue = new MyQueue();
for (int i = 0; i < k; i++) {
myQueue.add(nums[i]);
}
int index = 0;
resultNums[index++] = myQueue.peek();
for (int i = k; i < nums.length; i++) {//这里面会为resultNums更新nums.lengh-k个元素
//删除元素,如果nums[i - k]是最大值时,那么肯定是要干掉这个元素的,
// 如果不是最大值,那么myQueue里根本不可能存在nums[i - k],因为都被add方法干掉了
myQueue.pop(nums[i - k]);
myQueue.add(nums[i]);
resultNums[index++] = myQueue.peek();
}
return resultNums;
}
private int[] maxNums(int[] nums) {
int maxNum = nums[0];
for (int num : nums) {
if (maxNum < num) maxNum = num;
}
return new int[]{maxNum};
}
}
class MyQueue{
LinkedList<Integer> list = new LinkedList<>();
//返回索引为0的元素值
Integer peek(){
return list.peek();
}
void add(Integer val){
//保证最前面的最大
while (!list.isEmpty() && val > list.getLast()){
list.removeLast();
}
list.add(val);
}
void pop(Integer val){
if (!list.isEmpty() && val == list.peek()) list.pop();
}
}