如何将数组间隔中的所有值递增给定量

时间:2013-08-23 17:35:36

标签: algorithm data-structures range-query

假设我有一个长度为L的数组A.我将给出n个区间(i,j)并且我必须增加A [i]和A [j]之间的所有值。这个数据结构最适合于给定的操作?
间隔是事先已知的。

5 个答案:

答案 0 :(得分:16)

你可以得到O(N + M)。保持一个额外增量数组B与A的相同大小最初为空(填充0)。如果你需要用值k增加范围(i,j),那么做B [i] + = k和B [j + 1] - = k

现在在B中进行部分和变换,考虑到你是从0开始索引:

for (int i = 1; i < N; ++i) B[i] += B[i - 1];

现在A的最终值是A [i] + B [i]

答案 1 :(得分:6)

将所有时间间隔分为开始和结束索引:s_ie_i,第i个时间间隔开始包括s_i,结束时不包括e_i

将所有s_i - s排序为数组S. 将所有e_i - s排序为数组E

increment设置为零 开始输入的线性扫描并为每个人增加增量, 如果下一个s_iindex increment e_i

,那么下一个index是当前increment增量inc=0 s=<PriorityQueue of interval startindexes> e=<PriorityQueue of interval endindexes> for(i=0;i<n;i++){ if( inc == 0 ){ // skip adding zeros i=min(s.peek(),e.peek()) } while( s.peek() == i ) { s.pop(); inc++; } while( e.peek() == i ) { e.pop(); inc--; } a[i]+=inc; } 的每个循环中
O(n+m*log(m))

复杂性(不跳过非增量元素):n>>m // m是间隔数 如果O(n)则为O( min( n , \sum length(I_i) ) )

跳过元素时的复杂性:length(I_i)=e_i-s_i,其中{{1}}

答案 2 :(得分:3)

我可以想到三种主要方法:

方法1

这是最简单的一个,你只需按原样保存数组,并做一些天真的增量。

      
  • 优点:查询是恒定的时间   
  • 缺点:增量可以是线性时间(因此如果L很大则相当慢)

方法2

这个有点复杂,但如果你计划增加很多,那就更好了。

将元素存储在二叉树中,以便按顺序遍历按顺序访问元素。每个节点(除了正常的左子节点和右子节点之外)还存储一个额外的int addOn,当您查询此树中的任何节点时,它将“添加我”。

对于查询元素,在索引上执行常规二进制搜索以查找元素,并随时添加addOn变量的所有值。将它们添加到您想要的节点的A[i],这就是您的价值。

对于增量,遍历到树中,根据需要更新所有这些新addOns。请注意,如果您将增量值添加到一个节点的addOn,则不会为这两个子节点更新它。然后,每个增量的运行时间为O(log L),因为您必须“分支”到子项中的唯一时间是区间中的第一个或最后一个元素在您的范围内。因此,您最多分支2 log L次,并在元素中更多地访问常量因子。

       
  • 优点:增量现在是O(log L),所以现在如果你增加一吨,事情会比以前快得多。    
  • 缺点:查询需要更长时间(也是O(log L)),实现起来要复杂得多。

方法3

使用interval tree

       
  • 优点:就像方法2一样,这个可以比天真的方法快得多    
  • 缺点:如果您事先不知道间隔是什么,那就不可行了。
    执行起来也很棘手

答案 3 :(得分:0)

解决单个间隔的问题。然后迭代所有间隔并为每个间隔应用单间隔解决方案。最好的数据结构取决于语言。这是一个Java示例:

public class Interval {
    int i;
    int j;
}

public void increment(int[] array, Interval interval) {
    for (int i = interval.i; i < interval.j; ++i) {
        ++array[i];
    }
}

public void increment(int[] array, Interval[] intervals) {
    for (Interval interval : intervals) {
        increment(array, interval);
    }
}

如果你想减少代码量,显然你可以将一个循环嵌套在另一个循环中。但是,单区间方法本身可能很有用。

修改

如果事先知道间隔,那么你可以稍微改进一下。您可以修改Interval结构以维持增量(默认为1)。然后按如下方式预处理时间间隔 S

  1. 将第二组间隔 T 初始化为空集
  2. 对于 S 中的每个 I 区间:如果我没有重叠 T 中的任何区间,请添加 I T ;否则:
  3. 对于与 I 重叠的 T 中的每个时间间隔 J ,从 T中删除 J ,形成新的区间 K 1 ... K n from < em> I 和 J 使得没有重叠( n 可以是1到3),并添加 K < sub> 1 ... K n to T
  4. 完成后,使用 T 中的间隔和较早的代码(按照描述进行修改)。由于没有重叠,因此阵列的任何元素都不会增加多次。对于一组固定的时间间隔,无论数组长度如何,这都是常数时间算法。

    对于N个区间,通过保持按间隔开始索引排序的 T ,可以将拆分过程设计为以接近O(N log N)的值运行。但是,如果成本在许多阵列增量操作中分摊,那么这对整体复杂性来说并不是那么重要。

答案 4 :(得分:0)

Adrian Budau

建议的O(M + N)算法的可能实现
import java.util.Scanner;

class Interval{
    int i;
    int j;
}

public class IncrementArray {
    public static void main(String[] args) {
        int k= 5;                                   // increase array elements by this value
        Scanner sc = new Scanner(System.in);
        int intervalNo = sc.nextInt();                // specify no of intervals
        Interval[] interval = new Interval[intervalNo];           // array containing ranges/intervals
        System.out.println(">"+sc.nextLine()+"<");
        for(int i=0;i<intervalNo;i++)
        {
            interval[i]= new Interval();
            String s = sc.nextLine();                             // specify i and j separated by comma in one line for an interval.
            String[] s1 = s.split(" ");
            interval[i].i= Integer.parseInt(s1[0]);
            interval[i].j= Integer.parseInt(s1[1]);
        }
        int[] arr = new int[10];           // array whose values need to be incremented.
        for(int i=0;i<arr.length;++i)
            arr[i]=i+1;                    // initialising array.
        int[] temp = new int[10];
        Interval run=interval[0]; int i;
        for(i=0;i<intervalNo;i++,run=interval[i<intervalNo?i:0] )  // i<intervalNo?i:0 is used just to avoid arrayBound Exceptions at last iteration.
        {
            temp[run.i]+=k;
            if(run.j+1<10)                                          // incrementing temp within array bounds.
            temp[run.j +1]-=k;
        }
        for (i = 1; i < 10; ++i) 
            temp[i] += temp[i - 1];

        for(i=0, run=interval[i];i<10;i++)
            {
                arr[i]+= temp[i];
                System.out.print(" "+arr[i]);                     // printing results.
            }


    }
}