求滑动窗口中最大的值

题目:给定一个数组nums,有一个滑动大小为k的窗口从左往右滑动。每一次走一步,

求窗口中最大的值。

分享三种思路:

1.是暴力解法

       每次滑动窗口,遍历窗口中k个数的最大值。

public int[] maxSlidingWindow(int[] nums, int k) {
		if (nums == null || nums.length == 0) {
			return new int[0];
		}
		if (k == 1) {
			return nums;
		}
		int[] res = new int[nums.length - k + 1];

		for (int i = 0; i < nums.length - k + 1; i++) {
			int max = Integer.MIN_VALUE;

			for (int j = i; j < i + k; j++) {
				max = Math.max(nums[j], max);
			}
			res[i] = max;
		}
		return res;
	}

时间复杂度为 O(n*k)

 

2. 用双向队列实现

思想用双向队列保存窗口中的数的索引。 如果新滑入窗口的数比窗口左边的数大,就从队列中清除掉索引。

如图所示,窗口中数字为4,1,2,3。索引分别为2,3,4,5

在3新滑入到窗口时, 3的左边的1,2比3小,一定不会出现在窗口最大值中,所有做的操作是在双向队列中删除这些索引。如图所示的双向队列。这样双向队列每次最左边的数字永远是保存的是最大数的数组索引坐标。

public int[] maxSlidingWindow(int[] nums, int k) {
		if (nums == null || nums.length == 0) {
			return new int[0];
		}
		if (k == 1) {
			return nums;
		}
		int[] res = new int[nums.length - k + 1];
		ArrayDeque<Integer> deq = new ArrayDeque<Integer>();
		int index = 0;
		for (int i = 0; i < nums.length; i++) {
			if (i >= k && deq.getFirst() <= i - k) {  // 如果双向列表索引,超过边界就清理
				deq.removeFirst();
			}
			while (deq.size() > 0 && nums[deq.getLast()] <= nums[i]) { //删除比最右边数小的索引
				deq.removeLast();
			}
			deq.add(i); //加入到双向队列
			if (i >= k - 1) {
				res[index++] = nums[deq.getFirst()]; //双向队列头,永远是保存的窗口最大值的索引
			}
		}
		return res;
	}

时间复杂度为O(n)

 

3. 动态规划

算法的思想很巧妙,就是难的想到。这个方法是参考力扣上官方解法讲解的。写下来记录下来思想和代码。参考

算法的思想是把数组分成n块。

image.png

开头元素为 i ,结尾元素为 j 的当前滑动窗口可能在一个块内,也可能在两个块中。

image.png

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

 建立反向数组right,是从块的结束到下标index最大元素,方向 右 ->左。  

情况2,可以有两部分组成,一个是左块的一部分,加上右块的一部分。如下所示:

左边的一部分用反向数组right记录最大值,右边的一部分用left数组记录最大值。

最后求两个数组的最大值,即为窗口最大值。

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];
		left[0] = nums[0];
		int[] right = new int[n];
		right[n - 1] = nums[n - 1];
		for (int i = 1; i < n; i++) {
			// left
			if (i % k == 0) {
				left[i] = nums[i];
			} else {
				left[i] = Math.max(left[i - 1], nums[i]);
			}
			// right
			if ((n - i) % k == 0) {
				right[n - i - 1] = nums[n - i - 1];
			} else {
				right[n - i - 1] = Math.max(nums[n - i - 1], right[n - i]);
			}
		}

		int[] res= new int[n - k + 1];
		for(int i = k - 1 ; i < n ; i++) {
			res[i - k + 1] = Math.max(left[i], right[i - k + 1]);
		}

		return res;
	}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值