给定阵列V,我们需要找到两个索引(i,j),使得V [j]> V [i]和(j-i)是最大的

时间:2011-11-10 21:02:13

标签: algorithm

给定阵列V,我们需要找到两个索引(i,j),使得V [j]> V [i]和(j - i)是最大值。

蛮力方法非常简单,其中对于索引i处的每个值(范围从1到n),我们比较索引j处的值(范围从i + 1到n)。到目前为止,我们跟踪最大值(j-i)以找到最终答案。

这种方法的时间复杂度为O(n ^ 2)。有没有人提出改善时间复杂度的建议?

5 个答案:

答案 0 :(得分:3)

这是一种可以在线性时间内解决问题的方法。

  1. 计算增加位置i的堆栈S,使min A[1..i-1] > i在阵列上进行简单的向前扫描。
  2. 向后迭代列表。
  3. 当前元素大于堆栈顶部给出的值S:检查我们是否有新记录并弹出堆栈顶部。
  4. python中的快速实现:

    def notsurewhattonamethis(A):
        assert(A)
        S = [0]
        for i,v in enumerate(A):
            if v<A[S[-1]]:
                S.append(i)
        best = (-1,-1)
        for i,v in reversed(list(enumerate(A))):
            while v>A[S[-1]]:
                j = S.pop()
                d = i - j
                if d > best[1]-best[0]:
                    best = (j,i)
                if not S: return best
        return best
    

答案 1 :(得分:2)

如果您已知数组元素的限制(请参阅下面的更新)我可以建议您使用时间复杂度O(n*log(MaxN))和空间复杂度O(MaxN)的算法,其中MaxN = Max(V) [一世])。 对于该算法,我们需要能够在时间复杂度为O(log(N))的情况下在1和N之间的数组中得到最小的结构,并且更新具有时间复杂度O(log(N))的数组元素。 Fenwick tree可以做那些技巧。我们称这个结构为 minimizator 。然后我们需要:

  1. 按给定顺序迭代所有元素v [i]并将v [i]位置置于最小值i。
  2. 对于每个元素v [i]在1和v [i-1]之间找到最小使用最小值(这是小于v [i]的元素的最小索引)
  3. 记住i和找到的最小元素索引之间的最大差异小于v [i]。
  4. 确定。我试着写一些伪代码

    prepare array (map values)
    init minimizator
    
    ansI = -1
    ansJ = -1
    
    for i from 0 to v.length-1
      minIndexOfElementLessThanCurrent = get min value from 1 to v[i]-1 (inclusive) using minimizator
      set to minimizator v[i] position value i
    
      if minIndexOfElementLessThanCurrent is exists
        if ansJ - ansI < i - minIndexOfElementLessThanCurrent 
          ansJ = i
          ansI = minIndexOfElementLessThanCurrent
    

    C ++实现:

    class FenwickTree
    {
        vector<int> t;
        int n;
    
    public:
    
        static const int INF = 1000*1000*1000;
    
        void Init (int n)
        {
            this->n = n;
            t.assign (n, INF);
        }
    
        int GetMin (int i)
        {
            int res = INF;
            for (; i >= 0; i = (i & (i+1)) - 1)
                res = min (res, t[i]);
            return res;
        }
    
        void Update (int i, int value)
        {
            for (; i < n; i = (i | (i+1)))
                t[i] = min (t[i], value);
        }
    };
    
    pair<int, int> Solve(const vector<int>& v)
    {
        int maxElement = 0;
        for(int i = 0; i < v.size(); i++)
            maxElement = max(maxElement, v[i]);
    
        FenwickTree minimizator;
        minimizator.Init(maxElement+1);
    
        int ansI = -1, ansJ = -1;
        for(int i = 0; i < v.size(); i++)
        {
            int minLeftIndex = minimizator.GetMin(v[i]-1);      
            minimizator.Update(v[i], i);
    
            if(minLeftIndex == FenwickTree::INF) continue; // no left elements less than v[i]
    
            if(ansJ - ansI < i - minLeftIndex)
            {           
                ansJ = i;
                ansI = minLeftIndex;
            }
        }
        return make_pair(ansI, ansJ);
    }
    

    <强>更新 如果元素类型不是int(f.e。double)或者数组元素的max值太大(f.e. 10 ^ 9)我们可以 将数组值(不会影响结果)映射到整数集1..N,然后时间复杂度应为 O(n * log(n))

    <强>更新

    如果元素是整数 - 则有O(max(maxN, n))个解决方案。因此,如果maxN <= n复杂度为 O(N)。我们只需要回答查询'在const时间O(1)中从最小值1到N':

    1. 创建大小为maxN
    2. 的数组
    3. 数组的元素m [i]是源数组V中i值的最小索引。
    4. 使用动态编程创建数组,其大小与数组r[i]的最小值m[j], 1 <= j <= i相同。递归关系为r[i] = min(r[i-1], m[i])
    5. 此算法的主要思想与上述相同,仅使用数组r查找从1v[i]的最小值。

      C++ implementation:
      
      pair<int, int> Solve(const vector<int>& v)
      {
          int maxElement = 0;
          for(int i = 0; i < v.size(); i++)
              maxElement = max(maxElement, v[i]);
      
          vector<int> minimum(maxElement + 1, v.size() + 1);
          for(int i = 0; i < v.size(); i++)
              minimum[v[i]] = min(minimum[v[i]], i); // minimum[i] contains minimum index of element i
      
          for(int i = 1; i < minimum.size(); i++)
              minimum[i] = min(minimum[i-1], minimum[i]); // now minimum[i] contains minimum index between elements 1 and i
      
          int ansI = -1, ansJ = -1;
          for(int i = 0; i < v.size(); i++)
          {
              int minLeftIndex = minimum[v[i]-1];      
      
              if(minLeftIndex >= i) continue; // no left elements less than v[i]
      
              if(ansJ - ansI < i - minLeftIndex)
              {           
                  ansJ = i;
                  ansI = minLeftIndex;
              }
          }
          return make_pair(ansI, ansJ);
      }
      

      如果元素是double或其他(非常大的整数),我们无法映射元素以在线性时间内设置1..N(或者可以?)。我只知道O(n*log(n))解决方案(排序元素等)

答案 2 :(得分:2)

Java实现以线性时间运行。

public class MaxIndexDifference {

public static void main(String[] args) {
     System.out.println(betweenTwoElements(2, 3, 6, 10, 4));
}

private static int betweenTwoElements(int... nums) {
    int numberOfElements = nums.length;
    int maxDifference = -1, minIndex = 0, maxIndex = 0;

    int[] lMin = new int[numberOfElements];
    int[] rMax = new int[numberOfElements];

    /* Construct lMin such that stores the minimum value (to the left)  from (nums[0], nums[1], ... nums[i])*/

    lMin[0] = nums[0];
    for (int i = 1; i < numberOfElements; i++) {
        lMin[i] = Math.min(nums[i], lMin[i -1]);
    }
    /* Construct RMax[] such that RMax[j] stores the maximum value (to the right) from (arr[j], arr[j+1], ..arr[n-1]) */
    rMax[numberOfElements - 1] = nums[numberOfElements - 1];
    for (int i = numberOfElements-2; i >= 0; i--) {
        rMax[i] =  Math.max(nums[i], rMax[i + 1]);
    }
    /* Traverse both arrays from left to right to find optimum maxIndex - minIndex This process is similar to merge() of MergeSort */
    while (minIndex < numberOfElements && maxIndex < numberOfElements) {
        if (lMin[minIndex] < rMax[maxIndex]) {
            maxDifference = Math.max(maxDifference, maxIndex - minIndex);
            maxIndex = maxIndex +1;
        } else {
            minIndex = minIndex +1;
        }           
    }   
    return maxDifference;
}
}

答案 3 :(得分:1)

具有O(N)复杂度的算法:

#!/usr/bin/perl

use strict;
use warnings;

sub dump_list { print join(", ", map sprintf("%2d", $_), @_), "\n" }

for (0..20) {
    # generate a random list of integers with some convenient bias:
    my @l = (map int(rand(20) + 20 - $_), 0..19);

    my $max = $l[-1];
    my $min = $l[0];

    my @max;
    for my $l (reverse @l) {
        $max = $l if $l > $max;
        push @max, $max;
    }
    @max = reverse @max;

    my @min;
    for my $l (@l) {
        $min = $l if $l < $min;
        push @min, $min;
    }

    my $best_i = 0;
    my $best_j = -1;
    my $best   = -1;

    my $j = 0;
    for my $i (0..$#l) {
        while ($j < @l) {
            last unless $max[$j] > $min[$i];
            $j++;
            if ($j - $i > $best) {
                $best = $j - 1 - $i;
                $best_i = $i;
                $best_j = $j - 1;
            }
        }
    }

    print "list: "; dump_list @l;
    print "idxs: "; dump_list 0..$#l;
    print "best: $best, i: $best_i, j: $best_j\n\n";
}

更新:响应Nohsib请求:

假设您有一个随机的数字列表(a [0],[1],[2],[3] ......,[N-1])

第一步是找到左边最大的每个数字为mas max[i] = maximum(a[0], a[1], ..., a[i]),最小值为min[i] = minimum(a[i], a[i+1], ..., a[N-1])

让这些数组找到最大化a[j] < a[k]的{​​{1}}的间隔几乎是微不足道的。

尝试使用随机列表在纸上进行,您将很容易找到背后的逻辑。

答案 4 :(得分:0)

我不确定你真正想要的是什么,你问题的第一段与第二段冲突。所以我会给出两个答案,一个是我能想到的每个解释,尽管两者可能都不是你的意思。

第一种解释:在j> 1的约束下搜索最大V [j] -V [i]。我。
这就是几乎找到最小值和最大值。但另外,索引也存在约束。这本身并不意味着不能使用这个想法;对于任何选择的V [i],你只需要超过V [i + 1 .. n],并且你不需要每次重新计算这些最大值,导致这个算法:

  • 计算O(n)
  • 中的suffix-max数组
  • 找到O(n)中最大差异的对(V [i],suffixmax [i + 1])

第二种解释:在V [j]>的约束下搜索最大j-i。 V [i]中。
我无法想出任何好的东西。