我有一个n个元素的数组,对于每个元素,我试图找到最后k个值中的最高值(包括当前值)?例如。给出k,
int[] highestValues = new int[arr.Length];
for (int i = k - 1; i < arr.Length; i++)
{
int highest = Int32.MinValue;
for (int j = i - k; j <= i; j++)
{
highest = Math.Max(highest, arr[j]);
}
highestValues[i] = highest;
}
该算法的复杂性似乎是O(n ^ 2)。我想知道是否有任何方法可以更快地完成,例如而不是比较内部循环中的k个数字,重用上一个操作的结果。
答案 0 :(得分:2)
这个问题有一个很好的解决方案,使用 Dequeue 数据结构(在第一个和最后一个位置支持添加和检查元素的结构)。
我们创建类Entry来存储元素的索引和该元素的值。观察时间复杂度约为2 * n = O(2 * n)= O(n)(每个元素只添加一次到队列中,我们也遍历队列中的每个元素一次:在队列的第一个或最后一个位置删除它。
伪代码:
class Entry {
int value, index;
}
int [] highestValues;
Dequeue queue = new Dequeue();
for(int i = 0; i < arr.Length; i++){
Entry en = new Entry(arr[i], i);
while(queue.last().value <= en.value){//Remove all elements smaller than the current element.
queue.removeLast();
}
queue.append(en);
if(i >= k){
highestValues[i] = queue.first().value;
while(queue.first().index < i - k){//Remove all elements which has index out of the range
queue.removeFirst();
}
}
}
}
答案 1 :(得分:1)
这是最大滑动窗口问题或滑动窗口中的最大值数组。
请参阅:
关键是双端队列允许从开始和结束中删除元素,以便在我们滑动时保持最大值。
来源示例:
import java.util.ArrayDeque;
import java.util.Deque;
公共类SlidingWindow {
public static void maxSlidingWindow(int A[], int n, int w, int B[]) {
Deque<Integer> Q = new ArrayDeque<Integer>();
// Initialize deque Q for first window
for (int i = 0; i < w; i++) {
while (!Q.isEmpty() && A[i] >= A[Q.getLast()])
Q.pollLast();
Q.offerLast(i);
}
for (int i = w; i < n; i++) {
B[i - w] = A[Q.getFirst()];
// update Q for new window
while (!Q.isEmpty() && A[i] >= A[Q.getLast()])
Q.pollLast();
// Pop older element outside window from Q
while (!Q.isEmpty() && Q.getFirst() <= i - w)
Q.pollFirst();
// Insert current element in Q
Q.offerLast(i);
}
B[n - w] = A[Q.getFirst()];
}
public static void main(String args[]) {
int k = 20;
int a[] = new int[100];
for(int i=0; i<100; i++)
a[i] = i;
int b[] = new int[a.length - k + 1];
maxSlidingWindow(a, a.length, k, b);
System.out.println("Sliding Window Maximum is ");
for (int i = 0; i < b.length; i++) {
System.out.print(b[i] + ",");
}
}
}
答案 2 :(得分:0)
我有一个我认为有效的实现(用C#编码):
public static int[] LastKHighestValues(int[] array, int k)
{
int[] maxes = new int[array.Length];
int indexOfCurrentMax = 0;
int indexOfNextMax = 0;
for (int i = 0; i < array.Length; i++)
{
if (indexOfNextMax <= indexOfCurrentMax ||
array[i] > array[indexOfNextMax])
{
indexOfNextMax = i;
}
if (i - indexOfCurrentMax >= k)
{
indexOfCurrentMax = indexOfNextMax;
}
if (array[i] > array[indexOfCurrentMax])
{
indexOfCurrentMax = i;
}
maxes[i] = array[indexOfCurrentMax];
}
return maxes;
}
这个想法是你保留两个“指针”:一个指向当前最大值,一个指向当前最大值到期后的下一个指针。这可以在一次通过数组中实现(所以O(n))。
我有一些通过测试,但我很难确定我已经涵盖了每个角落的情况:
Puzzle.LastKHighestValues(new[] {4, 3, 1}, 1).Should().Equal(new[] {4, 3, 1});
Puzzle.LastKHighestValues(new[] { 7, 7, 7 }, 3).Should().Equal(new[] { 7, 7, 7 });
Puzzle.LastKHighestValues(new[] { 7, 7, 7 }, 4).Should().Equal(new[] { 7, 7, 7 });
Puzzle.LastKHighestValues(new[] { 3, 2, 1 }, 2).Should().Equal(new[] { 3, 3, 2 });
Puzzle.LastKHighestValues(new[] { 7, 1, 4, 1, 1 }, 3).Should().Equal(new[] { 7, 7, 7, 4, 4 });
Puzzle.LastKHighestValues(new[] { 7, 8, 4, 9, 10 }, 2).Should().Equal(new[] { 7, 8, 8, 9, 10 });