算法:生成窗口最大值数组
标签(空格分隔): 算法
版本:1
作者:陈小默
声明:禁止商用,禁止转载
有数组[2,3,4,3,2,5,6],窗口尺寸为3,设窗口滑动方向为从左向右。得到下列路径图
[2 3 4] 3 2 5 6
2 [3 4 3] 2 5 6
2 3 [4 3 2] 5 6
2 3 4 [3 2 5] 6
2 3 4 3 [2 5 6]
沿路径取窗口最大值得数组[4,4,4,5,6],要求在长度为N的数组和S指定的窗口尺寸在求得窗口最大值数组。
思路:这里可以使用沿路径判断窗口内的最大值的方式,但是这种方式的时间复杂度为 (N−S+1)∗S 也就是 O(N∗S) ,那么我们需要新的方式来将时间复杂度降低至 O(N) 。通过观察可以发现规律:由于窗口是从左向右滑动,所以在一个窗口中最大值左侧的数没有存在意义,推广开来也就是一个窗口中比右侧任意数字小的数都没有存在的意义。于是我们可以使用一个链表,这个链表用来存储有意义的数字的下标,那么在每一次窗口滑动的时候链表的第一位一定是该窗口最大值的下标。于是就将该题的解决思路转换为了求得每个窗口中有意义的数字的角标。
使用上面的例子:
在第一个窗口中有意义数字的下标是{2},那么最大值就是索引为2的数字4;
在第二个窗口中有意义的数字下标是{2,3},于是最大值就是链表中第一位为索引的数字4;
在第三个窗口中有意义的数字下标是{2,3,4},最大值就是链表中第一位为索引的数字4;
在第四个窗口中有意义的数字下标是{5},最大值就是链表中第一位为索引的数字5;
在第五个窗口中有意义的数字下标是{6},最大值就是链表中第一位为索引的数字6。
至于如何求有意义数字的下标,我们可以采取以下方式:
对于某一个索引i
- 链表为空,直接添加索引i
- 假设链表最后一个数字为j,如果
- arr[i]>=arr[j],移除链表中的最后一位,重复此步骤。
- arr[i]<arr[j],直接将i添加到链表的最后一位。
实现:
/**
* Copyright (C) <2016> <陈小默>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Created by 陈小默 on 11/06.
*/
fun genMaxArray(array: IntArray, winSize: Int): IntArray {
val maxIndList = LinkedList<Int>()
var j = 0
val maxArray = IntArray(array.size - winSize + 1, { 0 })
for (i in 0..array.size - 1) {
if (!maxIndList.isEmpty() && i - winSize == maxIndList.first) maxIndList.removeFirst()
while (!maxIndList.isEmpty() && array[maxIndList.last] < array[i])
maxIndList.removeLast()
maxIndList.add(i)
if (i >= winSize - 1) maxArray[j++] = array[maxIndList.first]
}
return maxArray
}