Maximum prefix-sum for a given range
Last Updated :
27 Mar, 2023
Given an array of n integers and q queries, each query having a range from l to r. Find the maximum prefix-sum for the range l – r.
Examples:
Input: a[] = {-1, 2, 3, -5}
q = 2
l = 0, r = 3
l = 1, r = 3
Output: 4
5
Explanation:- The range (0, 3) in the 1st query has
[-1, 2, 3, -5], since it is prefix,
we have to start from -1. Hence, the
max prefix sum will be -1 + 2 + 3 = 4.
The range (1, 3) in the 2nd query has
[2, 3, -5], since it is prefix, we
have to start from 2. Hence, the max
prefix sum will be 2 + 3 = 5.
Input: a[] = {-2, -3, 4, -1, -2, 1, 5, -3}
q = 1
l = 1 r = 7
Output: 4
Explanation:- The range (1, 7) in the 1st query has
[-3, 4, -1, -2, 1, 5, -3], since it is
prefix, we have to start from -3.
Hence, the max prefix sum will be
-3 + 4 - 1 - 2 + 1 + 5 = 4.
Normal Approach: A simple solution is to run a loop from l to r and calculate max prefix sum from l to r for every query.
C++
#include <bits/stdc++.h>
using namespace std;
int ans(vector< int > arr, int l, int r)
{
int prefix_sum = 0;
int max_prefix_sum = INT_MIN;
for ( int i = l; i <= r; i++) {
prefix_sum += arr[i];
max_prefix_sum = max(max_prefix_sum, prefix_sum);
}
return max_prefix_sum;
}
int main()
{
vector< int > arr = { -1, 2, 3, -5 };
cout << ans(arr, 0, 3) << endl;
cout << ans(arr, 1, 3) << endl;
return 0;
}
|
Java
import java.util.*;
public class Main {
public static int ans(List<Integer> arr, int l, int r)
{
int prefix_sum = 0 ;
int max_prefix_sum = Integer.MIN_VALUE;
for ( int i = l; i <= r; i++) {
prefix_sum += arr.get(i);
max_prefix_sum
= Math.max(max_prefix_sum, prefix_sum);
}
return max_prefix_sum;
}
public static void main(String[] args)
{
List<Integer> arr = Arrays.asList(- 1 , 2 , 3 , - 5 );
System.out.println(ans(arr, 0 , 3 ));
System.out.println(ans(arr, 1 , 3 ));
}
}
|
Python3
def ans(arr, l, r):
prefix_sum = 0
max_prefix_sum = float ( '-inf' )
for i in range (l, r + 1 ):
prefix_sum + = arr[i]
max_prefix_sum = max (max_prefix_sum, prefix_sum)
return max_prefix_sum
if __name__ = = '__main__' :
arr = [ - 1 , 2 , 3 , - 5 ]
print (ans(arr, 0 , 3 ))
print (ans(arr, 1 , 3 ))
|
C#
Javascript
function ans(arr, l, r) {
let prefix_sum = 0;
let max_prefix_sum = -Infinity;
for (let i = l; i <= r; i++) {
prefix_sum += arr[i];
max_prefix_sum = Math.max(max_prefix_sum, prefix_sum);
}
return max_prefix_sum;
}
let arr = [-1, 2, 3, -5];
console.log(ans(arr, 0, 3));
console.log(ans(arr, 1, 3));
|
Time Complexity: O(q * n),
Auxiliary Space: O(1)
Efficient Approach:
An efficient approach will be to build a segment tree where each node stores two values(sum and prefix_sum), and do a range query on it to find the max prefix sum. But for building the segment tree we have to think about what to store on the nodes of the tree.
For finding out the maximum prefix sum, we will require two things, one being the sum and the other prefix sum. The merging will return two things, sum of the ranges and the prefix sum that will store the max(prefix.left, prefix.sum + prefix.right) in the segment tree.
If we have a deep look into it, the max prefix sum for any two range combining will either be the prefix sum from left side or the sum of left side+prefix sum of right side, whichever is max is taken into account.
Representation of Segment trees:
- Leaf Nodes are the elements of the input array.
- Each internal node represents some merging of the leaf nodes. The merging may be different for different problems. For this problem, merging is sum of leaves under a node.
An array representation of tree is used to represent Segment Trees. For each node at index i, the left child is at index 2*i+1, right child at 2*i+2 and the parent is at (i-1)/2.
Construction of Segment Tree from given array:
We start with a segment arr[0 . . . n-1]. and every time we divide the current segment into two halves(if it has not yet become a segment of length 1), and then call the same procedure on both halves, and for each such segment, we store the sum and prefix sum in corresponding node.
We then do a range query on segment tree to find out the max prefix-sum for the given range, and output the max prefix-sum.
Below is the C++ implementation of above approach.
CPP
#include <bits/stdc++.h>
using namespace std;
struct Node {
int sum;
int prefix;
};
Node tree[4 * 10000];
void build( int a[], int index, int beg, int end)
{
if (beg == end) {
tree[index].sum = a[beg];
tree[index].prefix = a[beg];
} else {
int mid = (beg + end) / 2;
build(a, 2 * index + 1, beg, mid);
build(a, 2 * index + 2, mid + 1, end);
tree[index].sum = tree[2 * index + 1].sum +
tree[2 * index + 2].sum;
tree[index].prefix = max(tree[2 * index + 1].prefix,
tree[2 * index + 1].sum +
tree[2 * index + 2].prefix);
}
}
Node query( int index, int beg, int end, int l, int r)
{
Node result;
result.sum = result.prefix = -1;
if (beg > r || end < l)
return result;
if (beg >= l && end <= r)
return tree[index];
int mid = (beg + end) / 2;
if (l > mid)
return query(2 * index + 2, mid + 1, end,
l, r);
if (r <= mid)
return query(2 * index + 1, beg, mid,
l, r);
Node left = query(2 * index + 1, beg, mid,
l, r);
Node right = query(2 * index + 2, mid + 1,
end, l, r);
result.sum = left.sum + right.sum;
result.prefix = max(left.prefix, left.sum +
right.prefix);
return result;
}
int main()
{
int a[] = { -2, -3, 4, -1, -2, 1, 5, -3 };
int n = sizeof (a) / sizeof (a[0]);
build(a, 0, 0, n - 1);
cout << query(0, 0, n - 1, 3, 5).prefix
<< endl;
cout << query(0, 0, n - 1, 1, 7).prefix
<< endl;
return 0;
}
|
Java
class GFG
{
static class Node
{
int sum;
int prefix;
};
static Node []tree = new Node[ 4 * 10000 ];
static
{
for ( int i = 0 ; i < tree.length; i++)
tree[i] = new Node();
}
static void build( int a[], int index, int beg, int end)
{
if (beg == end)
{
tree[index].sum = a[beg];
tree[index].prefix = a[beg];
} else {
int mid = (beg + end) / 2 ;
build(a, 2 * index + 1 , beg, mid);
build(a, 2 * index + 2 , mid + 1 , end);
tree[index].sum = tree[ 2 * index + 1 ].sum +
tree[ 2 * index + 2 ].sum;
tree[index].prefix = Math.max(tree[ 2 * index + 1 ].prefix,
tree[ 2 * index + 1 ].sum +
tree[ 2 * index + 2 ].prefix);
}
}
static Node query( int index, int beg, int end, int l, int r)
{
Node result = new Node();
result.sum = result.prefix = - 1 ;
if (beg > r || end < l)
return result;
if (beg >= l && end <= r)
return tree[index];
int mid = (beg + end) / 2 ;
if (l > mid)
return query( 2 * index + 2 , mid + 1 , end,
l, r);
if (r <= mid)
return query( 2 * index + 1 , beg, mid,
l, r);
Node left = query( 2 * index + 1 , beg, mid,
l, r);
Node right = query( 2 * index + 2 , mid + 1 ,
end, l, r);
result.sum = left.sum + right.sum;
result.prefix = Math.max(left.prefix, left.sum +
right.prefix);
return result;
}
public static void main(String[] args)
{
int a[] = { - 2 , - 3 , 4 , - 1 , - 2 , 1 , 5 , - 3 };
int n = a.length;
build(a, 0 , 0 , n - 1 );
System.out.print(query( 0 , 0 , n - 1 , 3 , 5 ).prefix
+ "\n" );
System.out.print(query( 0 , 0 , n - 1 , 1 , 7 ).prefix
+ "\n" );
}
}
|
C#
using System;
class GFG
{
class Node
{
public int sum;
public int prefix;
};
static Node []tree = new Node[4 * 10000];
static void build( int []a, int index, int beg, int end)
{
if (beg == end)
{
tree[index].sum = a[beg];
tree[index].prefix = a[beg];
} else {
int mid = (beg + end) / 2;
build(a, 2 * index + 1, beg, mid);
build(a, 2 * index + 2, mid + 1, end);
tree[index].sum = tree[2 * index + 1].sum +
tree[2 * index + 2].sum;
tree[index].prefix = Math.Max(tree[2 * index + 1].prefix,
tree[2 * index + 1].sum +
tree[2 * index + 2].prefix);
}
}
static Node query( int index, int beg, int end, int l, int r)
{
Node result = new Node();
result.sum = result.prefix = -1;
if (beg > r || end < l)
return result;
if (beg >= l && end <= r)
return tree[index];
int mid = (beg + end) / 2;
if (l > mid)
return query(2 * index + 2, mid + 1, end,
l, r);
if (r <= mid)
return query(2 * index + 1, beg, mid,
l, r);
Node left = query(2 * index + 1, beg, mid,
l, r);
Node right = query(2 * index + 2, mid + 1,
end, l, r);
result.sum = left.sum + right.sum;
result.prefix = Math.Max(left.prefix, left.sum +
right.prefix);
return result;
}
public static void Main(String[] args)
{
for ( int i = 0; i < tree.Length; i++)
tree[i] = new Node();
int []a = { -2, -3, 4, -1, -2, 1, 5, -3 };
int n = a.Length;
build(a, 0, 0, n - 1);
Console.Write(query(0, 0, n - 1, 3, 5).prefix
+ "\n" );
Console.Write(query(0, 0, n - 1, 1, 7).prefix
+ "\n" );
}
}
|
Python3
def build(a, index, beg, end):
global tree
if (beg = = end):
tree[index][ 0 ] = a[beg]
tree[index][ 1 ] = a[beg]
else :
mid = (beg + end) / / 2
build(a, 2 * index + 1 , beg, mid)
build(a, 2 * index + 2 , mid + 1 , end)
tree[index][ 0 ] = tree[ 2 * index + 1 ][ 0 ] + tree[ 2 * index + 2 ][ 0 ]
tree[index][ 1 ] = max (tree[ 2 * index + 1 ][ 1 ],tree[ 2 * index + 1 ][ 0 ] + tree[ 2 * index + 2 ][ 1 ])
def query(index, beg, end, l, r):
global tree
result = [ - 1 , - 1 ]
if (beg > r or end < l):
return result
if (beg > = l and end < = r):
return tree[index]
mid = (beg + end) / / 2
if (l > mid):
return query( 2 * index + 2 , mid + 1 , end, l, r)
if (r < = mid):
return query( 2 * index + 1 , beg, mid, l, r)
left = query( 2 * index + 1 , beg, mid, l, r)
right = query( 2 * index + 2 , mid + 1 , end, l, r)
result[ 0 ] = left[ 0 ] + right[ 0 ]
result[ 1 ] = max (left[ 1 ], left[ 0 ] + right[ 1 ])
return result
if __name__ = = '__main__' :
a = [ - 2 , - 3 , 4 , - 1 , - 2 , 1 , 5 , - 3 ]
tree = [[ 0 , 0 ] for i in range ( 4 * 10000 )]
n = len (a)
build(a, 0 , 0 , n - 1 )
print (query( 0 , 0 , n - 1 , 3 , 5 )[ 1 ])
print (query( 0 , 0 , n - 1 , 1 , 7 )[ 1 ])
|
Javascript
<script>
class Node
{
constructor()
{
let sum,prefix;
}
}
let tree= new Array(4 * 10000);
for (let i = 0; i < tree.length; i++)
tree[i] = new Node();
function build(a,index,beg,end)
{
if (beg == end)
{
tree[index].sum = a[beg];
tree[index].prefix = a[beg];
} else {
let mid = Math.floor((beg + end) / 2);
build(a, 2 * index + 1, beg, mid);
build(a, 2 * index + 2, mid + 1, end);
tree[index].sum = tree[2 * index + 1].sum +
tree[2 * index + 2].sum;
tree[index].prefix =
Math.max(tree[2 * index + 1].prefix,
tree[2 * index + 1].sum +
tree[2 * index + 2].prefix);
}
}
function query(index,beg,end,l,r)
{
let result = new Node();
result.sum = result.prefix = -1;
if (beg > r || end < l)
return result;
if (beg >= l && end <= r)
return tree[index];
let mid = Math.floor((beg + end) / 2);
if (l > mid)
return query(2 * index + 2, mid + 1, end,
l, r);
if (r <= mid)
return query(2 * index + 1, beg, mid,
l, r);
let left = query(2 * index + 1, beg, mid,
l, r);
let right = query(2 * index + 2, mid + 1,
end, l, r);
result.sum = left.sum + right.sum;
result.prefix = Math.max(left.prefix, left.sum +
right.prefix);
return result;
}
let a=[-2, -3, 4, -1, -2, 1, 5, -3];
let n = a.length;
build(a, 0, 0, n - 1);
document.write(query(0, 0, n - 1, 3, 5).prefix
+ "<br>" );
document.write(query(0, 0, n - 1, 1, 7).prefix
+ "<br>" );
</script>
|
Time Complexity:
Time Complexity for tree construction is O(n). There are total 2n-1 nodes, and value of every node is calculated only once in tree construction.
Time complexity to every query is O(log n).
Time complexity for the problem is O(q * log n)
Space Complexity: O(n)
Share your thoughts in the comments
Please Login to comment...