JAVA程序设计:滑动窗口最大值(LeetCode:239)

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

返回滑动窗口中的最大值。

 

示例:

输入: 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
 

提示:

你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。

 

进阶:

你能在线性时间复杂度内解决此题吗?

思路:

方案一:优先队列,每次只存k个值,然后每次弹出对头元素(该元素的下标不能小于当前遍历的位置)

class Solution {
	
	class node implements Comparator<node>
	{
		int val,index;
		public node() {}
		public node(int val,int index)
		{
			this.val=val;
			this.index=index;
		}
		
		public int compare(node a,node b)
		{
			if(val==b.val)
			{
				if(index==b.index)
					return 0;
				return index<b.index?1:-1;
			}
			return a.val<b.val?1:-1;		
		}
	}
	
    public int[] maxSlidingWindow(int[] nums, int k) {
    	
    	if(nums.length==0) return new int[0];
    	
    	Comparator<node> queueComparable=new node();
        PriorityQueue<node> q=new PriorityQueue<node>(nums.length,queueComparable);
        int[] ans=new int[nums.length-k+1];
        for(int i=0;i<k;i++)
        	 q.add(new node(nums[i],i));
        ans[0]=q.peek().val;
        for(int i=1;i<nums.length-k+1;i++)
        {
        	while(!q.isEmpty() && q.peek().index<i)
        		q.poll();
        	if(i+k-1<nums.length)
        		q.add(new node(nums[i+k-1],i+k-1));
        	ans[i]=q.peek().val;
        }
        
        return ans;
    }
}

方法二:双端队列

处理前 k 个元素,初始化双向队列。

遍历整个数组。在每一步 :

清理双向队列 :

  - 只保留当前滑动窗口中有的元素的索引。

  - 移除比当前元素小的所有元素,它们不可能是最大的。

将当前元素添加到双向队列中。
将 deque[0] 添加到输出中。
返回输出数组。

class Solution {
	
	ArrayDeque deq=new ArrayDeque<Integer>();
	int[] nums;
	
    public int[] maxSlidingWindow(int[] nums, int k) {
    	int n=nums.length;
    	if(n*k==0) return new int[0];
    	if(k==1) return nums;
    	
    	this.nums=nums;
    	int max_idx=0;
    	for(int i=0;i<k;i++)
    	{
    		clean_deque(i,k);
    		deq.addLast(i);
    		
    		if(nums[i]>nums[max_idx])
    			max_idx=i;
    	}
    	
    	int[] output=new int[n-k+1];
    	output[0]=nums[max_idx];
    	
    	for(int i=k;i<n;i++)
    	{
    		clean_deque(i,k);
    		deq.addLast(i);
    		output[i-k+1]=nums[(int)deq.getFirst()];
    	}
    	
    	return output;
    }
    
    private void clean_deque(int i,int k)
    {
    	if(!deq.isEmpty() && (int)deq.getFirst()==i-k)
    		deq.removeFirst();
    	
    	while(!deq.isEmpty() && nums[i]>nums[(int)deq.getLast()])
    		deq.removeLast();	
    }
}

方法三:动态规划

建立数组 left, 其中 left[j] 是从块的开始到下标 j 最大的元素,方向 左->右

需要数组 right,其中 right[j] 是从块的结尾到下标 j 最大的元素,方向 右->左right 数组和 left 除了方向不同以外基本一致。

算法十分直截了当:

从左到右遍历数组,建立数组 left。

从右到左遍历数组,建立数组 right。

建立输出数组 max(right[i], left[i + k - 1]),其中 i 取值范围为 (0, n - k + 1)。

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
    	int n=nums.length;
    	if(n*k==0) return new int[0];
    	if(k==1) return nums;
    	
    	int [] left=new int[n];
    	int[] right=new int[n+1];
    	right[n-1]=nums[n-1];
    	
    	for(int i=0;i<n;i++)
    	{
    		if(i%k==0) left[i]=nums[i];
    		else left[i]=Math.max(left[i-1], nums[i]);
    		
    		int j=n-i-1;
    		if((j+1)%k==0) right[j]=nums[j];
    		else right[j]=Math.max(right[j+1], nums[j]);
    	}
    	
    	int[] output=new int[n-k+1];
    	for(int i=0;i<n-k+1;i++)
    		output[i]=Math.max(left[i+k-1], right[i]);
    	
    	return output;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值