给定一个由n个数字组成的数组,任务是回答以下查询:
maximumSubarraySum(start, end) : Find the maximum
subarray sum in the range from array index 'start'
to 'end'.
另请参阅:需要更新的范围查询
例子:
Input : arr[] = {1, 3, -4, 5, -2}
Query 1: start = 0, end = 4
Query 2: start = 0, end = 2
Output : 5
4
Explanation:
For Query 1, [1, 3, -4, 5] or ( [5] )
represent the maximum sum sub arrays
with sum = 5.
For Query 2, [1, 3] represents the
maximum sum subarray in the query range
with sum = 4
段树可以用来解决这个问题,这里我们需要保留关于各种累计和的信息,在每个节点上我们存储以下内容:
1)最大前缀总和,
2)最大后缀总和,
3)总和,
4)最大子数组总和
每个节点都存储上述信息的经典分段树应足以查询每个查询。这里唯一的重点是如何将树的左右节点合并在一起。现在,我们将讨论如何使用其左,右子级的信息在每个段树节点中构造每个信息。
使用Left和Right子项构造最大前缀和
节点的最大前缀和可能有两种情况:
- 最大前缀和出现在左子节点中,
In this Case, Maximum Prefix Sum = Maximum Prefix Sum of Left Child
- 最大前缀和包含左子元素的每个数组元素,以及有助于右子元素的最大前缀和的元素,
In this Case, Maximum Prefix Sum = Total Sum of Left Child + Maximum Prefix Sum of Right Child
使用Left和Right子项构造最大后缀和
节点的最大后缀和可能有两种情况:
- 最大后缀总和出现在右孩子中,
In this Case, Maximum Suffix Sum = Maximum Suffix Sum of Right Child
- 最大后缀总和包含Right子元素的每个数组元素,以及构成左子元素的最大后缀和的元素,
In this Case, Maximum Suffix Sum = Total Sum of Right Child + Maximum Suffix Sum of Left Child
使用Left和Right子项构造最大子数组总和
节点的最大子数组和可能有以下三种情况:
- 最大子数组总和出现在左子元素中,
In this Case, Maximum Sub-array Sum = Maximum Subarray Sum of Left Child
- 最大子数组总和出现在右子元素中,
In this Case, Maximum Sub-array Sum = Maximum Subarray Sum of Right Child
- 最大子数组总和包含右侧子项的数组元素和右侧子项的最大后缀和,其中右侧子项的数组元素对右侧子项的最大前缀和有贡献,左子项的数组元素对左子项的最大后缀和有贡献,
In this Case, Maximum Subarray Sum = Maximum Prefix Sum of Right Child + Maximum Suffix Sum of Left Child
// C++ Program to Implement Maximum Sub-Array Sum in a range
#include
using namespace std;
#define inf 0x3f3f
/* Node of the segment tree consisting of:
1. Maximum Prefix Sum,
2. Maximum Suffix Sum,
3. Total Sum,
4. Maximum Sub-Array Sum */
struct Node {
int maxPrefixSum;
int maxSuffixSum;
int totalSum;
int maxSubarraySum;
Node()
{
maxPrefixSum = maxSuffixSum = maxSubarraySum = -inf;
totalSum = -inf;
}
};
// Returns Parent Node after merging its left and right child
Node merge(Node leftChild, Node rightChild)
{
Node parentNode;
parentNode.maxPrefixSum = max(leftChild.maxPrefixSum,
leftChild.totalSum +
rightChild.maxPrefixSum);
parentNode.maxSuffixSum = max(rightChild.maxSuffixSum,
rightChild.totalSum +
leftChild.maxSuffixSum);
parentNode.totalSum = leftChild.totalSum +
rightChild.totalSum;
parentNode.maxSubarraySum = max({leftChild.maxSubarraySum,
rightChild.maxSubarraySum,
leftChild.maxSuffixSum +
rightChild.maxPrefixSum});
return parentNode;
}
// Builds the Segment tree recursively
void constructTreeUtil(Node* tree, int arr[], int start,
int end, int index)
{
/* Leaf Node */
if (start == end) {
// single element is covered under this range
tree[index].totalSum = arr[start];
tree[index].maxSuffixSum = arr[start];
tree[index].maxPrefixSum = arr[start];
tree[index].maxSubarraySum = arr[start];
return;
}
// Recursively Build left and right children
int mid = (start + end) / 2;
constructTreeUtil(tree, arr, start, mid, 2 * index);
constructTreeUtil(tree, arr, mid + 1, end, 2 * index + 1);
// Merge left and right child into the Parent Node
tree[index] = merge(tree[2 * index], tree[2 * index + 1]);
}
/* Function to construct segment tree from given array.
This function allocates memory for segment tree and
calls constructTreeUtil() to fill the allocated
memory */
Node* constructTree(int arr[], int n)
{
// Allocate memory for segment tree
int x = (int)(ceil(log2(n))); // Height of the tree
// Maximum size of segment tree
int max_size = 2 * (int)pow(2, x) - 1;
Node* tree = new Node[max_size];
// Fill the allocated memory tree
constructTreeUtil(tree, arr, 0, n - 1, 1);
// Return the constructed segment tree
return tree;
}
/* A Recursive function to get the desired
Maximum Sum Sub-Array,
The following are parameters of the function-
tree --> Pointer to segment tree
index --> Index of the segment tree Node
ss & se --> Starting and ending indexes of the
segment represented by
current Node, i.e., tree[index]
qs & qe --> Starting and ending indexes of query range */
Node queryUtil(Node* tree, int ss, int se, int qs,
int qe, int index)
{
// No overlap
if (ss > qe || se < qs) {
// returns a Node for out of bounds condition
Node nullNode;
return nullNode;
}
// Complete overlap
if (ss >= qs && se <= qe) {
return tree[index];
}
// Partial Overlap Merge results of Left
// and Right subtrees
int mid = (ss + se) / 2;
Node left = queryUtil(tree, ss, mid, qs, qe,
2 * index);
Node right = queryUtil(tree, mid + 1, se, qs,
qe, 2 * index + 1);
// merge left and right subtree query results
Node res = merge(left, right);
return res;
}
/* Returns the Maximum Subarray Sum between start and end
It mainly uses queryUtil(). */
int query(Node* tree, int start, int end, int n)
{
Node res = queryUtil(tree, 0, n - 1, start, end, 1);
return res.maxSubarraySum;
}
int main()
{
int arr[] = { 1, 3, -4, 5, -2 };
int n = sizeof(arr) / sizeof(arr[0]);
// Construct Segment Tree
Node* Tree = constructTree(arr, n);
int start, end, maxSubarraySum;
// Answering query 1:
start = 0;
end = 4;
maxSubarraySum = query(Tree, start, end, n);
cout << "Maximum Sub-Array Sum between "
<< start << " and " << end
<< " = " << maxSubarraySum << "\n";
// Answering query 2:
start = 0;
end = 2;
maxSubarraySum = query(Tree, start, end, n);
cout << "Maximum Sub-Array Sum between "
<< start << " and " << end
<< " = " << maxSubarraySum << "\n";
return 0;
}
输出:
Maximum Sub-Array Sum between 0 and 4 = 5
Maximum Sub-Array Sum between 0 and 2 = 4
时间复杂度:每个查询为O(logn)。