2

In a range update question, I want to add a value to array elements in a range. It can be assumed that the array (or some data structure) is sorted.

A naive algorithm would be to loop through the starting element to the ending element and add a value v to each element.

But that takes O(n) time in the worst case when the range is from the first to last element. Is there a faster way to do this? Should I use segment tree to do it?

maregor
  • 777
  • 9
  • 32
  • 2
    If the array is sorted you can use binary search to locate the range – higuaro Apr 23 '15 at 22:43
  • Yes the array is sorted but what do you mean by that? – maregor Apr 23 '15 at 22:45
  • 1
    Unless you have some constraints on the range, this is by definition O(n). – YXD Apr 23 '15 at 22:47
  • Maybe I could store the information in a separate place? So that it knows which elements have been flagged, without having to go through the entire array? – maregor Apr 23 '15 at 22:48
  • Yes, if you need to add a value to n elements it will not take less than O(n) actions. What kind of stunts are you looking for ? – Nir Alfasi Apr 23 '15 at 22:53
  • 1
    I just read about lazy propagation in a segment tree. I think that could solve the problem. – maregor Apr 23 '15 at 22:56
  • 1
    Using a segment tree is an overkill. A Fenwick Tree is especially suited for this task and is much easier to implement. – Juan Lopes Apr 24 '15 at 01:18

4 Answers4

2

If you array is sorted, then you can quick search (for example, using binary search) any single element, belong to your range. Thereafter, add your increment value to elements before and after entry point.

For example, lets array contains ints. So code will be like:

int *p;
// try to fill array from begin to element range_max
if(your_array[0] >= range_min) {
  for(p = your_array; p < your_array + elems_qty; p++)
    if(*p <= range_max)
      *p += delta;
    else 
      break;
  return; // head of array filled
}

// try to fill array from end to element range_min
if(your_array[elms_qty - 1] <= range_max) {
  for(p = your_array + elms_qty - 1; p >= your_array; p--)
    if(*p >= range_min)
      *p += delta;
    else 
      break;
  return; // tail of array filled
}

// There is range somewhere inside array

int avg_element = (range_min + range_max) / 2;

int *avg_ptr = bsearch(&avg_element, your_array, elems_qty, sizeof(int), int_comparator);

for(p = avg_ptr; *p >= range_min; p--)
  *p += delta;

for(p = avg_ptr; *p <= range_max; p++)
  *p += delta;
olegarch
  • 3,670
  • 1
  • 20
  • 19
2

If speed matters and you operate only on closed ranges and add every time constant value, you can create "modification queue" where you put sequence of modifications, defined by (starting index, ending index, delta), keeping array itself untouched. Actual processing may be performed at the time of least activity.

This will make modification operate in guaranteed O(1), but random read cost will increase to O(K), where K is size of the queue (average number of modifications between subsequent flushes). If array is big and ranges are wide, but modifications occur not very often and must return quickly, such approach may win.

Alex Salauyou
  • 14,185
  • 5
  • 45
  • 67
  • Taking this idea a bit further, if you *also* store the modifications in an auxiliary size-n array by setting `aux[start] = delta` and `aux[end] = -delta`, and force every sqrt(n)th access (read or write) to perform a flush, then the queue is always O(sqrt(n)) and you get amortised O(sqrt(n)) per-access bounds for any sequence of reads and writes. Still not as good as using a segment tree or Fenwick tree, but interesting! – j_random_hacker Apr 24 '15 at 12:23
  • @j_random_hacker Yes, there are many ways to modify. Range tree storing modifications is the best I believe. – Alex Salauyou Apr 24 '15 at 12:33
  • @j_random_hacker I put this answer mostly to contradict commenters and show an opposite approach: *fast update, slow read* vs *slow update, fast read*. My intention was to show that the problem is not only about the cost of update, but more about actual usage scenarios. – Alex Salauyou Apr 24 '15 at 12:35
2

You can use a Fenwick Tree. It's way easier to implement than a segment tree. (It's called a tree, but it's actually implemented as an array, just like a binary heap).

Juan Lopes
  • 10,143
  • 2
  • 25
  • 44
1

If the array is really a c style array (not necessarily sorted) and not a tree like data structure which already implicitly includes its order in its structure O(n) is the best you can get.

There are other data structures (trees Of various sorts that would allow faster lookup. However, converting an unsorted array to one of these structures will always be worse than O(n).

Nick Bailey
  • 3,078
  • 2
  • 11
  • 13
  • So I actually have a freedom to decide on a tree data structure, so I can use a segment tree with lazy propagation to add the value. Thanks! – maregor Apr 23 '15 at 23:00
  • For this task, a tree structure doesn't per se give you a performance advantage over a binary search on an array. The slight advantage will come if you can modify the search to locate minimum and maximum in the same search. – Neil Coffey Apr 24 '15 at 00:15
  • 2
    Yes, a tree structure (segment tree, Fenwick tree) gives performance advantage if you need to perform several queries or if the construction is online. – Juan Lopes Apr 24 '15 at 02:21