假设我有一个长度为L的数组A.我将给出n个区间(i,j)并且我必须增加A [i]和A [j]之间的所有值。这个数据结构最适合于给定的操作?
间隔是事先已知的。
答案 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_i
,e_i
,第i个时间间隔开始包括s_i
,结束时不包括e_i
将所有s_i
- s排序为数组S.
将所有e_i
- s排序为数组E
将increment
设置为零
开始输入的线性扫描并为每个人增加增量,
如果下一个s_i
为index
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
这是最简单的一个,你只需按原样保存数组,并做一些天真的增量。
方法2
这个有点复杂,但如果你计划增加很多,那就更好了。
将元素存储在二叉树中,以便按顺序遍历按顺序访问元素。每个节点(除了正常的左子节点和右子节点之外)还存储一个额外的int addOn
,当您查询此树中的任何节点时,它将“添加我”。
对于查询元素,在索引上执行常规二进制搜索以查找元素,并随时添加addOn
变量的所有值。将它们添加到您想要的节点的A[i]
,这就是您的价值。
对于增量,遍历到树中,根据需要更新所有这些新addOns
。请注意,如果您将增量值添加到一个节点的addOn
,则不会为这两个子节点更新它。然后,每个增量的运行时间为O(log L)
,因为您必须“分支”到子项中的唯一时间是区间中的第一个或最后一个元素在您的范围内。因此,您最多分支2 log L
次,并在元素中更多地访问常量因子。
方法3
答案 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 :
完成后,使用 T 中的间隔和较早的代码(按照描述进行修改)。由于没有重叠,因此阵列的任何元素都不会增加多次。对于一组固定的时间间隔,无论数组长度如何,这都是常数时间算法。
对于N个区间,通过保持按间隔开始索引排序的 T ,可以将拆分过程设计为以接近O(N log N)的值运行。但是,如果成本在许多阵列增量操作中分摊,那么这对整体复杂性来说并不是那么重要。
答案 4 :(得分:0)
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.
}
}
}