C ++:在子数组的数组中查找最大整数
Posted
技术标签:
【中文标题】C ++:在子数组的数组中查找最大整数【英文标题】:C++: Find the maximum integer in an array of sub-arrays 【发布时间】:2016-05-25 21:47:18 【问题描述】:我遇到了一个问题,我想编写一个算法,该算法可以返回更大数组中每个连续的 k 个元素的子数组的最大元素,并将这些最大元素读入它们自己的数组,如下所示:
Given int array = 3, 7, 20, 6, 12, 2, 0, 99, 5, 16, and int k = 4,
--> creates the array 20, 20, 20, 12, 99, 99, 99
[because there are 7 consecutive sub-arrays of size 4 within the given array:
3, 7, 20, 6, 7, 20, 6, 12, 20, 6, 12, 2, ... , 0, 99, 5, 16
and the max element of these, respectively, is 20, 20, 20, ..., 99 which
are read into the resulting array.
现在这是我的问题:我知道如何在 O(n^2) 复杂度中实现这一点,但希望让它更快,以便它可以是 O(n),或者如果那是不可能,O(nlog(n))。有谁知道是否有更快的方法来做到这一点,如果有,怎么做?
【问题讨论】:
*连续子数组。对不起,我忘了提这个 我不认为你可以在执行复杂性方面提高效率,除非你有某种形式的启发式。如果这些数据结构是树,您可以使用高级截断算法,例如 alpha-beta 剪枝。所以不幸的是,我认为你只能使用递归使它更优雅,而你却被O(n^2)
你不是说 O(nk) 复杂度而不是 O(n^2) 吗?天真的方法似乎是扫描每个子数组中的 k 个元素并选择最大的。
Can min/max of moving window achieve in O(N)?的可能重复
【参考方案1】:
首先,朴素算法的复杂度是O(k(n-k+1))(通常这近似为O(kn)),而不是O(n^2)。这就是,对于每个连续的子数组(可能有 n-k+1 个),您必须执行 k 个比较。
您可以通过一些 memoization 做得比这更好,使用长度为 k 的附加数组,我们可以称之为 maximums
。该数组将存储下一个最大值的索引。
对于数据集的每次迭代,您都会检查maximums
的第一个元素。您删除所有“过期”索引,现在第一个元素是您当前迭代的答案。
当您在数据上滑动一个窗口(大小 k)时,您将当前索引推送到 maximums
,然后按如下方式对其进行修剪:索引处的值 maximums[i]
必须小于索引maximums[i-1]
处的值。如果不是,那么您继续将索引冒泡到 maximums
的开头,一次一个位置,直到这成为真的。
实际上,最好将maximums
数组视为环形缓冲区。修剪过程会将尾部向头部收缩,而弹出任何“过期”的最大值(当窗口滑过它们时)将使头部前进一步。
这有点笨拙,但这里有一些工作代码来说明:
#include <vector>
#include <iostream>
int main()
const int window_size = 4;
std::vector<int> vals = 3, 7, 20, 6, 12, 2, 0, 99, 5, 16 ;
std::vector<int> maximums( window_size );
int mhead = 0, mtail = 0;
for( int i = 1; i < vals.size(); i ++ )
// Clean out expired maximum.
if( maximums[mhead] + window_size <= i )
int next_mhead = (mhead + 1) % window_size;
if( mtail == mhead ) mtail = next_mhead;
mhead = next_mhead;
if( vals[i] >= vals[ maximums[mtail] ] )
// Replace and bubble up a new maximum value.
maximums[mtail] = i;
while( mhead != mtail && vals[ maximums[mtail] ] >= vals[ maximums[(mtail+window_size-1)%window_size] ] )
int prev_mtail = (mtail + window_size - 1) % window_size;
maximums[prev_mtail] = maximums[mtail];
mtail = prev_mtail;
else
// Add a new non-maximum.
mtail = (mtail + 1) % window_size;
maximums[mtail] = i;
// Output current maximum.
if( i >= window_size - 1 )
std::cout << vals[ maximums[mhead] ] << " ";
std::cout << std::endl;
return 0;
现在,时间复杂度...
最好的情况是 O(n),如果您的所有数据都已排序(升序或降序),就会发生这种情况。
我相信最坏的情况是 O(2n)。在一次迭代中需要 k 个额外操作的唯一方法是,如果您已经有 k 个线性复杂度步骤(因此环形缓冲区已满)。在这种情况下,下一步的环形缓冲区将是空的。由于我们只能填充和清空环形缓冲区 n/k 次,那些偶尔的 k 操作会在 kn/k 时出现,或者只是 kn/k强>n。
您应该能够证明,即使环形缓冲区持续部分清空也会导致相同的复杂性。
最后,我们可以总结并称整个事情为 O(n),因为对于较大的 n,任何常数因子都变得无关紧要。它实际上比我预期的要好。 =)
【讨论】:
我可能应该提到,像许多算法一样,朴素的方法可能更适合 k 的小值,但由于 k 是变大,线性时间算法的好处开始显现。以上是关于C ++:在子数组的数组中查找最大整数的主要内容,如果未能解决你的问题,请参考以下文章
从整数数组中找到大小为 K 的连续子数组,使得从 1 到 k 的附加元素永远不会低于零