Time:2019/4/16
Title: Sliding Window Maximum
Difficulty: Difficulty
Author: 小鹿
题目:Sliding Window Maximum
Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Return the max sliding window.
给定一个数组
nums,有一个大小为
k 的滑动窗口从数组的最左边挪动到数组的最右边。你只能够看到在滑动窗口
k 内的数字。滑动窗口每次只向右挪动一名。返回滑动窗口最大值。
Example:
Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3
Output: [3,3,5,5,6,7]
Explanation:
Window position Max
--------------- -----
[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
Note:
You may assume k is always valid, 1 ≤ k ≤ input array’s size for non-empty array.
Follow up:
Could you solve it in linear time?
Solve:
▉ 题目剖析
暴力破解法
1)看到这个题目最轻易想到的就是暴力破解法,借助一个 for 轮回,两个变量,团体挪动窗口,然后每挪动一次就在大小为 k 的窗口求出最大值。
2)然则如许的处理效力异常低,假如数据异常大时,共有 n1 个数据,窗口大小为 n2(n1 远远大于 n2),时候复杂度为 n2(n1 – n2) 。也就是 n1 * n2,最坏时候复杂度为 n^2。
优先级行列
1)每次挪动窗口求最大值,以及在动态数据中求最大值,我们想到的就是优先级行列,而优先级行列的完成是堆这类数据结构,这道题用堆处理效力更高。假如对堆不熟悉,赶忙给本身补补作业吧!底部有我写的文章链接。
2)经由过程堆的优化,向堆中插进去数据时候复杂度为 logn ,所以时候复杂度为 nlogn。
▉ 算法思绪
暴力破解法:
1)用两个指针,离别指向窗口的肇端位置和停止位置,然后遍历窗口中的数据,求出最大值;向前挪动两个指针,然后操纵,直到遍历数据完成位置。
优先级行列:
1)须要保护大小为 k 的大顶堆,堆顶就是当前窗口最大的数据,当挪动窗口时,假如插进去的数据大于堆顶的数据,将其加入到效果集合。同时要删除数据,假如删除的数据为最大数据且插进去的数据小于删除的数据时,向大小为 k 的以 logn 的时候复杂度插进去,返回堆顶元素。
▉ 暴力破解法
var maxSlidingWindow = function(nums, k) {
if(k > nums.length || k === 0) return [];
let res = [], maxIndex = -1;
for(let l = 0, r = k-1;r < nums.length;l++, r++){
if(maxIndex < l){
// 遍历求出最大值
let index = l;
for(let i = l;i <= r;i++) {
if(nums[i] > nums[index]) index = i;
}
maxIndex = index;
}
if(nums[r] > nums[maxIndex]){
maxIndex = r;
}
res.push(nums[maxIndex]);
}
return res;
};
▉ 优先级行列
let count = 0;
let heap = [];
let n = 0;
var maxSlidingWindow = function(nums, k) {
let pos = k;
n = k;
let result = [];
let len = nums.length;
// 推断数组和最大窗口树是不是为空
if(nums.length === 0 || k === 0) return result;
// 建大顶堆
let j = 0
for(;j < k; j++){
insert(nums[j]);
}
result.push(heap[1]);
// 挪动窗口
while(len - pos > 0){
if(nums[k] > heap[1]){
result.push(nums[k]);
insert(nums[k]);
nums.shift();
pos++;
}else{
if(nums.shift() === heap[1]){
removeMax();
}
insert(nums[k-1]);
result.push(heap[1]);
pos++;
}
}
return result;
};
// 插进去数据
const insert = (data) =>{
//推断堆满
// if(count >= n) return; // >=
// 插进去到数组尾部
count++
heap[count] = data;
//自下而上堆化
let i = count;
while(i / 2 > 0 && heap[i] > heap[parseInt(i/2)]){
swap(heap,i,parseInt(i/2));
i = parseInt(i/2);
}
}
// 两个数组内元素交流
swap = (arr,x,y) =>{
let temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
// 堆的删除
const removeMax = () =>{
// 推断堆空
if(count <= 0) return ;
// 最大数据移到末了删除
heap[1] = heap[count];
// 长度减一
count--;
// 删除数据
heap.pop();
// 从上到下堆化
heapify(heap,count,1);
}
// 从上到下堆化
const heapify = (heap,count,i) =>{
while(true){
// 存储堆子节点的最大值下标
let maxPos = i;
// 左子节点比父节点大
if(i*2 < n && heap[i*2] > heap[i]) maxPos = i*2;
// 右子节点比父节点大
if(i*2+1 <= n && heap[i*2+1] > heap[maxPos]) maxPos = i*2+1;
// 假如没有发生替代,则申明该堆只要一个结点(父节点)或子节点都小于父节点
if(maxPos === i) break;
// 交流
swap(heap,maxPos,i);
// 继承堆化
i = maxPos;
}
}
▉ 机能剖析
暴力破解法
- 时候复杂度:O(n^2).
- 空间复杂度:O(1).
优先级行列
- 时候复杂度:nlogn.
- 空间复杂度:O(1).
▉ 扩大
堆:
1)堆插进去、删除操纵
2)怎样完成一个堆?
3)堆排序
4)堆的运用
细致检察写的另一篇关于堆的文章:数据结构与算法之美【堆】
迎接一同加入到 LeetCode 开源 Github 堆栈,能够向 me 提交您其他言语的代码。在堆栈上对峙和小伙伴们一同打卡,配合完美我们的开源小堆栈!
Github:
https://github.com/luxiangqia…
迎接关注我个人民众号:「一个不甘寻常的码农」,记录了本身一起自学编程的故事。