Leetcode|Sliding Window Maximum(multiset,优先队列,双端队列和区间树的应用)

来源:互联网 发布:雨伞 淘宝 编辑:程序博客网 时间:2024/05/22 17:26

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.

For example,
Given nums = [1,3,-1,-3,5,3,6,7], and k = 3.

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

Therefore, return the max sliding window as [3,3,5,5,6,7].

1,暴力解法:最低效的时间复杂度O(k*(n-k+1));,

2,STL中的内置容器的排序功能。(O(n*logk))

为了熟悉STL,使用优先队列和nultiset来解决,虽然时间复杂度不是最优的。

//借助红黑树的排序方法,在leetcode击败15%的人,效率很低啦。
vector<int> maxSlidingWindow(vector<int>& nums, int k) {//利用multiset中红黑树的排序功能 时间复杂度O(nlogk)        int n=nums.size();        vector<int> max;        multiset<int> smax;        for(int i=0;i<n;i++){            if(i>=k) smax.erase(smax.find(nums[i-k]));//每次删除过期的一个元素,每次都得对k个数重新排序            smax.insert(nums[i]);            if(i>=k-1) max.push_back(*smax.rbegin());//找到排在最后的数,就是最大的        }        return max;    }
//借助STL的heap操作,击败了20%l的leetcoder。
vector<int> maxSlidingWindow(vector<int>& nums, int k) {//优先队列是一种配接器,底层是vector        int n=nums.size();        vector<int> max;        priority_queue<pair<int,int>> qmax;        for(int i=0;i<n;i++){            while(!qmax.empty()&&qmax.top().second<=i-k){                qmax.pop();            }            qmax.push(make_pair(nums[i],i));            if(i>=k-1) max.push_back(qmax.top().first);        }        return max;    }

3,线段树(时间复杂度O(n*log(k)) 击败了26%的leetcoder.

线段树节点的三个数值元素为:start,end,max(记录区间的最大值),是以一个vector作为底层容器实现。

class Solution {public:    vector<int> maxSlidingWindow(vector<int>& nums, int k) {        int n=nums.size();        vector<int> res;        if(k<=0||n==0||k>n) return res;        SegmentTreeNode *root=build_segmentTree(0,k-1,nums);        res.push_back(root->max);        for(int i=k;i<n;i++){            modify(i%k,nums[i],root);              res.push_back(root->max);        }        return res;    }private:    struct SegmentTreeNode{        int start,end,max;        SegmentTreeNode *left,*right;        SegmentTreeNode(int x,int y,int m):start(x),end(y),max(m),left(NULL),right(NULL){ }    };        SegmentTreeNode* build_segmentTree(int start,int end,vector<int>& a){        if(start>end||a.size()==0||end>=a.size()) return NULL;        SegmentTreeNode* root=new SegmentTreeNode(start,end,a[start]);        if(start==end) return root;        int mid=(start+end)/2;        root->left=build_segmentTree(start,mid,a);        root->right=build_segmentTree(mid+1,end,a);        root->max=max(root->left->max,root->right->max);        return root;    }        void modify(int dot,int newval,SegmentTreeNode* root){        if(dot<root->start||dot>root->end) return;        if(root->start==root->end){            root->max=newval;            return;        }        int mid=(root->start+root->end)/2;        if(dot<=mid) modify(dot,newval,root->left);        else modify(dot,newval,root->right);        root->max=max(root->left->max,root->right->max);    }    };

4,,最优解O(n):双端队列 击败50%leetcoder

deque来存储下标值,这样可以获得元素大小和位置两组信息。队列元素个数永远小于等于k,从大到小排序。击败50%的leetcoder

vector<int> maxSlidingWindow(vector<int>& nums, int k) {        int n=nums.size();        vector<int> max;        deque<int> qmax;        for(int i=0;i<n;i++){            while(!qmax.empty()&&nums[qmax.back()]<=nums[i]){//队尾的值实时更新                qmax.pop_back();            }            qmax.push_back(i);            if(qmax.front()==i-k){//更新过期的队首                 qmax.pop_front();            }            if(i>=k-1){                max.push_back(nums[qmax.front()]);            }        }        return max;    }


0 0