作算法题了,题的难度咱们分为“士,尉,校,将”四个等级。这个算法题的模块是篇幅比较小的那种模块。首先是给出一道题的描述,以后我会用个人想法来作这道题,今天算是算法题的第一道题,先来试试水。算法
有一个整型数组arr和一个大小为w的窗口从数组的最左边滑到最右边,窗口每次向右边滑一个位置。 例如,数组为[4,3,1,5,4,3,7,5],窗口大小为5时: [4 3 1 5 4] 3 7 5 max = 5 4 [3 1 5 4 3] 7 5 max = 5 4 3 [1 5 4 3 7] 5 max = 7 4 3 1 [5 4 3 7 5] max = 7 即窗口最大值数组为 result = {5, 5,7,7}
对于一道题,我通常会第一时间想到用暴力的方法来作,以后再来慢慢优化。数组
显然,对于这道题用暴力法来作仍是挺简单了,窗口每次向右移动一位时,咱们每次遍历窗口内的w个元素,而后求出此时窗口的最大值就能够了,用这种方法的时间复杂度是 O(wn)。代码以下:微信
//暴力法求解 public static int[] getMaxWindow(int[] arr, int w) { if (w < 1 || arr == null || arr.length < w) { return null; } int[] result = new int[arr.length - w + 1]; int index = 0; //暴力求解直接从第 w-1个元素开始遍历 for (int i = w - 1; i < arr.length; i++) { int max = arr[i]; //找出最大值 for (int k = i; k > i - w; k--) { if (max < arr[k]) { max = arr[k]; } } result[index++] = max; } return result; }
你们想一个问题,例如对于刚才例题中的数组:ide
第一次遍历的时候,max = 5学习
第二次遍历的时候,max = 5优化
咱们刚才用暴力法的时候,不管是第一次仍是第二次,咱们都是把窗口内的全部元素都给遍历了一次,以此来寻找最大值,但是,真的须要这样吗?3d
第一次遍历的时候,咱们找出了max = 5, 那么在第二次遍历的时候,在窗口范围内,max = 5 左边的两个数1, 3 还有多是最大值吗?也就是说,max=5 左边的窗口元素还要必要遍历吗?code
显然,max=5左边的窗口其实是没必要再遍历的了,也就是它不可能会是窗口的最大值。blog
而 max = 5 右边的 4 有可能会是窗口的最大值吗?因为窗口还会一直向右移动,因此 max = 5 右边的窗口元素仍是有多是某一个窗口的最大值的。队列
所以,咱们能够用一个双向的队列,来记录有可能成为窗口最大值的下标,注意,这里指的是有可能。
像刚才的 max = 5 前面的 1,3 就不可能成为窗口的最大值了,而右边的4仍是有可能成为窗口的最大值的。而且这个队列是有序的,队首存放的老是队列中的最大值,
我以这道题来演示一下,咱们用result[] 数组来存放窗口最大值。
一、result[0] = 5
二、result[1] = 5;
三、result[2] = 7
其余的所有都要出队,由于7前面的5,4,3是不可能成为窗口最大值的了。
四、result[3] = 7
遍历完毕。这种方法的话时间复杂度是 O(n)。
我这里只是提供了思路与大体的作法,具体的代码实现仍是有不少细节须要注意的。下面给出实现代码,代码会有详细的解释。
//优化 public static int[] getMaxWindow2(int[] arr, int w) { if (w < 1 || arr == null || arr.length < w) { return null; } //用来保存成为最大窗口的元素 int[] result = new int[arr.length - w + 1]; int index = 0; //用链表从当双向队列。 LinkedList<Integer> temp = new LinkedList<>(); //刚才演示的时候,我i直接从i = w-1那里开始演示了。 for (int i = 0; i < arr.length; i++) { //若是队列不为空,而且存放在队尾的元素小于等于当前元素,那么 //队列的这个元素就能够弹出了,由于他不可能会是窗口最大值。 //【当前元素】指的是窗口向右移动的时候新加入的元素。 while (!temp.isEmpty() && arr[temp.peekLast()] <= arr[i]) { temp.pollLast();//把队尾元素弹出 } //把【当前元素】的下边加入到队尾 temp.addLast(i); //若是队首的元素不在窗口范围内,则弹出 if (temp.peekFirst() == i - w) { temp.pollFirst();// } if (i >= w - 1) { //因为队首存放的是最大值,因此队首老是对应窗口的最大值元素 result[index++] = arr[temp.peekFirst()]; } } return result; }
说实话,微信看代码确实有点难受,若是是在电脑浏览的话还好点,我在考虑要不要用截图的方式,不过若是是截图的话,有些人想要复制代码的话会复制不了,那我以后考虑把代码打包,大家后台回复获取。