数组

时间:2015-08-13 01:57:08

标签: algorithm array-algorithms

如何查找/存储长度为n的数组的所有可能非空子数组的最大值/最小值?

我生成了数组的分段树,并为每个可能的子数组生成了查询到分段树但是效率不高的情况。我如何在O(n)中完成?

P.S n< = 10 ^ 7

For eg. arr[]= { 1, 2, 3 };  // the array need not to be sorted

sub-array     min      max
{1}            1        1
{2}            2        2
{3}            3        3
{1,2}          1        2
{2,3}          2        3
{1,2,3}        1        3

5 个答案:

答案 0 :(得分:2)

我认为在O(n)中存储所有这些值是不可能的。但是在O(n)中创建一个能够回答的结构非常容易,在O(1)中查询“A [i]是最大元素的子集有多少”。

Naïve版本:

想想天真的策略:要知道一些A [i]有多少这样的子集,你可以使用一个简单的O(n)算法来计算数组左边和右边有多少个元素。小于A [i]。让我们说:

A = [... 10 1 1 1 5 1 1 10 ...]

这个5向上有3个元素在左边,2到右边比它小。据我们所知,有4*3=12子阵列非常5是最大的。 4*3因为左侧有0..3个子阵列,右侧有0..2个。

优化版本:

这个天真的检查版本会对每个元素进行O(n)操作,所以毕竟是O(n ^ 2)。如果我们能够在一次通过中计算O(n)中的所有这些长度,那不是很好吗?

幸运的是,有一个简单的算法。只需使用堆栈。正常遍历阵列(从左到右)。将每个元素索引放在堆栈中。但在放入之前,请删除其值小于当前值的所有索引。当前一个之前的剩余索引是最近的较大元素。

要在右侧找到相同的值,只需向后遍历数组。

这是一个Python概念验证示例,显示了该算法的实际应用。我还实现了天真的版本,因此我们可以交叉检查优化版本的结果:

from random import choice
from collections import defaultdict, deque

def make_bounds(A, fallback, arange, op):
    stack = deque()
    bound = [fallback] * len(A)
    for i in arange:
        while stack and op(A[stack[-1]], A[i]):
            stack.pop()
        if stack:
            bound[i] = stack[-1]
        stack.append(i)
    return bound

def optimized_version(A):
    T = zip(make_bounds(A, -1, xrange(len(A)), lambda x, y: x<=y), 
            make_bounds(A, len(A), reversed(xrange(len(A))), lambda x, y: x<y))

    answer = defaultdict(lambda: 0)
    for i, x in enumerate(A):
        left, right = T[i]
        answer[x] += (i-left) * (right-i)
    return dict(answer)

def naive_version(A):
    answer = defaultdict(lambda: 0)
    for i, x in enumerate(A):
        left = next((j for j in range(i-1, -1, -1) if A[j]>A[i]), -1)
        right = next((j for j in range(i+1, len(A)) if A[j]>=A[i]), len(A))
        answer[x] += (i-left) * (right-i)
    return dict(answer)


A = [choice(xrange(32)) for i in xrange(8)]    
MA1 = naive_version(A)
MA2 = optimized_version(A)

print 'Array:    ', A
print 'Naive:    ', MA1
print 'Optimized:', MA2
print 'OK:       ', MA1 == MA2

答案 1 :(得分:1)

我不认为可以在O(n)时间内直接进行:你需要迭代子数组的所有元素,并且你有n个。除非对子阵列进行排序。

另一方面,你可以在初始化子阵列时,而不是使它们成为正常数组,你可以构建堆,特别是当你想要找到最大值时找到最小和最大堆时最小堆。< / p>

Building a heap is a linear time operation,并且分别检索最大堆和最小堆的最大值和最小值是一个常量时间操作,因为这些元素位于堆的第一个位置。

只需使用普通数组即可轻松实现堆。

在维基百科上查看有关二进制堆的文章:https://en.wikipedia.org/wiki/Binary_heap

答案 2 :(得分:0)

我不明白你最大的子阵列是什么意思,所以我假设你要求下列其中一个

  1. 最大/最小长度或其他一些标准的子阵列(在这种情况下,问题将减少到在一维数组中找到最大元素)
  2. 在一个子数组的上下文中或在整个超级数组的上下文中所有子数组的最大元素
  3. 问题1可以通过简单地迭代超级数组并存储对最大元素的引用来解决。或者像nbro说的那样建立堆。问题2也有类似的解决方案。然而,线性扫描通过n长度为m的数组不会是线性的。因此,您必须保持类不变量,以便在每次操作后都知道最大值/最小值。也许借助一些像堆这样的数据结构。

答案 3 :(得分:0)

假设你的意思是连续的子数组,创建部分和的数组,其中Yi = SUM(i = 0..i)Xi,所以从1,4,2,3创建0,1,1 + 4 = 5 ,1 + 4 + 2 = 7,1 + 4 + 2 + 3 = 10。您可以在线性时间内从左到右创建此值,并且任何连续子阵列的值都是从另一个中减去的一个部分和,因此4 + 2 + 3 = 1 + 4 + 2 + 3 - 1 = 9.

然后从左到右扫描部分和,跟踪到目前为止看到的最小值(包括初始零)。在每个点从当前值中减去它并跟踪以这种方式产生的最高值。这应该为您提供具有最大总和的连续子数组的值,并且您也可以保留索引信息以查找此子数组的开始和结束位置。

要找到最小值,要么稍微更改上面的内容,要么只是反转所有数字的符号并再次做同样的事情:min(a,b)= -max(-a,-b)

答案 4 :(得分:-1)

我认为您要问的问题是找到一个子目标的最大值。 bleow是在O(n)时间内做的代码。

int maxSumSubArr(vector<int> a)
{
    int maxsum = *max_element(a.begin(), a.end());
    if(maxsum < 0) return maxsum;
    int sum = 0;
    for(int i = 0; i< a.size; i++)
    {
      sum += a[i];
      if(sum > maxsum)maxsum = sum;
      if(sum < 0) sum = 0;
    }
    return maxsum;
}

注意:此代码未经过测试,如果发现某些问题,请添加评论。