📜  使用分而治之的最大和子数组|套装2

📅  最后修改于: 2021-04-21 23:54:26             🧑  作者: Mango

给定整数数组arr [] ,任务是在所有可能的子数组中找到最大和子数组。
例子:

方法:到目前为止,我们仅了解Kadane算法,该算法使用动态编程解决了O(n)中的这一问题。
我们还讨论了O(N * logN)时间复杂度中最大和子数组的分治法。
以下方法使用分而治之的方法解决了该问题,该方法采用了O(n)相同的时间复杂度
分而治之算法通常涉及将问题分为子问题,然后分别加以解决。对于这个问题,我们维护一个结构(在cpp中)或类(在Java或Python),该结构存储以下值:

  1. 子数组的总和。
  2. 子数组的最大前缀和。
  3. 子数组的最大后缀和。
  4. 子阵列的总最大和(包含子阵列的最大和)。

在递归过程中(分割部分),数组从中间分成两部分。左节点结构包含上述数组左部分的所有上述值,而右节点结构包含上述所有值。同时拥有两个节点,现在我们可以通过计算结果节点的所有值来合并两个节点。
结果节点的最大前缀总和将是左侧节点或左侧节点总和的最大前缀总和+右侧节点最大前缀总和或两个节点的总和之中的最大值(这对于具有所有正值的数组都是可能的) 。
类似地,结果节点的最大后缀总和将是右节点或右节点总和的最大后缀总和+左节点的最大后缀总和或两个节点的总和之中的最大值(这对于具有所有正数的数组也是可能的值)。
结果节点的总和为左节点和右节点之和。
现在,结果节点的最大子数组总和将在结果节点的前缀总和,结果节点的后缀总和,结果节点的总和,左节点的最大总和,右节点的最大总和,后缀的总和之中最大。左节点和右节点的最大前缀和。
在这里,可以通过组合左右节点结构的结果在O(1)时间内完成征服部分。
下面是上述方法的实现:

C++
// C++ implementation of the approach
#include
using namespace std;
 
struct Node {
     
    // To store the maximum sum
    // for a sub-array
    long long _max;
     
    // To store the maximum prefix
    // sum for a sub-array
    long long _pre;
     
    // To store the maximum suffix
    // sum for a sub-array
    long long _suf;
     
    // To store the total sum
    // for a sub-array
    long long _sum;
 
};
 
 
// Function to create a node
Node getNode(long long x){
    Node a;
    a._max = x;
    a._pre = x;
    a._suf = x;
    a._sum = x;
    return a;
}
 
// Function to merge the 2 nodes left and right
Node merg(const Node &l, const Node &r){
     
    // Creating node ans
    Node ans ;
 
    // Initializing all the variables:
    ans._max = ans._pre = ans._suf = ans._sum = 0;
     
    // The max prefix sum of ans Node is maximum of
    // a) max prefix sum of left Node
    // b) sum of left Node + max prefix sum of right Node
    // c) sum of left Node + sum of right Node
    ans._pre = max({l._pre, l._sum+r._pre, l._sum+r._sum});
 
    // The max suffix sum of ans Node is maximum of
    // a) max suffix sum of right Node
    // b) sum of right Node + max suffix sum of left Node
    // c) sum of left Node + sum of right Node
    ans._suf = max({r._suf, r._sum+l._suf, l._sum+r._sum});
     
    // Total sum of ans Node = total sum of left Node + total sum of right Node
    ans._sum = l._sum + r._sum;
     
    // The max sum of ans Node stores the answer which is the maximum value among:
    // prefix sum of ans Node
    // suffix sum of ans Node
    // maximum value of left Node
    // maximum value of right Node
    // prefix value of right Node + suffix value of left Node
    ans._max = max({ans._pre, ans._suf, ans._sum,l._max, r._max, l._suf+r._pre});
 
    // Return the ans Node
    return ans;
}
 
// Function for calculating the
// max_sum_subArray using divide and conquer
Node getMaxSumSubArray(int l, int r, vector &ar){
 
    if (l == r) return getNode(ar[l]);
 
    int mid = (l + r) >> 1;
     
    // Call method to return left Node:
    Node left = getMaxSumSubArray(l, mid, ar);
     
    // Call method to return right Node:
    Node right = getMaxSumSubArray(mid+1, r, ar);
     
    // Return the merged Node:
    return merg(left, right);
 
}
 
// Driver code
int main(){
 
    vector ar = {-2, -5, 6, -2, -3, 1, 5, -6};
    int n = ar.size();
    Node ans = getMaxSumSubArray(0, n-1, ar);
    cout << "Answer is " << ans._max << "\n";
 
    return 0;
}


Java
// Java implementation of the approach
import java.util.*;
class GFG
{
static class Node
{
     
    // To store the maximum sum
    // for a sub-array
    int _max;
     
    // To store the maximum prefix
    // sum for a sub-array
    int _pre;
     
    // To store the maximum suffix
    // sum for a sub-array
    int _suf;
     
    // To store the total sum
    // for a sub-array
    int _sum;
 
};
 
 
// Function to create a node
static Node getNode(int x)
{
    Node a = new Node();
    a._max = x;
    a._pre = x;
    a._suf = x;
    a._sum = x;
    return a;
}
 
// Function to merge the 2 nodes left and right
static Node merg(Node l, Node r)
{
     
    // Creating node ans
    Node ans = new Node();
 
    // Initializing all the variables:
    ans._max = ans._pre = ans._suf = ans._sum = 0;
     
    // The max prefix sum of ans Node is maximum of
    // a) max prefix sum of left Node
    // b) sum of left Node + max prefix sum of right Node
    // c) sum of left Node + sum of right Node
    ans._pre = Arrays.stream(new int[]{l._pre, l._sum+r._pre,
                                       l._sum+r._sum}).max().getAsInt();
 
    // The max suffix sum of ans Node is maximum of
    // a) max suffix sum of right Node
    // b) sum of right Node + max suffix sum of left Node
    // c) sum of left Node + sum of right Node
    ans._suf = Arrays.stream(new int[]{r._suf, r._sum+l._suf,
                                       l._sum+r._sum}).max().getAsInt();
     
    // Total sum of ans Node = total sum of
  // left Node + total sum of right Node
    ans._sum = l._sum + r._sum;
     
    // The max sum of ans Node stores
    // the answer which is the maximum value among:
    // prefix sum of ans Node
    // suffix sum of ans Node
    // maximum value of left Node
    // maximum value of right Node
    // prefix value of right Node + suffix value of left Node
    ans._max = Arrays.stream(new int[]{ans._pre,
                                       ans._suf,
                                       ans._sum,
                                       l._max, r._max,
                                       l._suf+r._pre}).max().getAsInt();
 
    // Return the ans Node
    return ans;
}
 
// Function for calculating the
// max_sum_subArray using divide and conquer
static Node getMaxSumSubArray(int l, int r, int []ar)
{
 
    if (l == r) return getNode(ar[l]);
    int mid = (l + r) >> 1;
     
    // Call method to return left Node:
    Node left = getMaxSumSubArray(l, mid, ar);
     
    // Call method to return right Node:
    Node right = getMaxSumSubArray(mid + 1, r, ar);
     
    // Return the merged Node:
    return merg(left, right);
 
}
 
// Driver code
public static void main(String[] args)
{
    int []ar = {-2, -5, 6, -2, -3, 1, 5, -6};
    int n = ar.length;
    Node ans = getMaxSumSubArray(0, n - 1, ar);
    System.out.print("Answer is " +  ans._max + "\n");
}
}
 
// This code is contributed by shikhasingrajput


Python3
# Python3 implementation of the approach
 
class Node:
     
    def __init__(self, x):
         
        # To store the maximum sum for a sub-array
        self._max = x
         
        # To store the maximum prefix sum for a sub-array
        self._pre = x
         
        # To store the maximum suffix sum for a sub-array
        self._suf = x
         
        # To store the total sum for a sub-array
        self._sum = x
         
# Function to merge the 2 nodes left and right
def merg(l, r):
     
    # Creating node ans
    ans = Node(0)
 
    # The max prefix sum of ans Node is maximum of
    # a) max prefix sum of left Node
    # b) sum of left Node + max prefix sum of right Node
    # c) sum of left Node + sum of right Node
    ans._pre = max(l._pre, l._sum+r._pre, l._sum+r._sum)
 
    # The max suffix sum of ans Node is maximum of
    # a) max suffix sum of right Node
    # b) sum of right Node + max suffix sum of left Node
    # c) sum of left Node + sum of right Node
    ans._suf = max(r._suf, r._sum+l._suf, l._sum+r._sum)
     
    # Total sum of ans Node = total sum of
    # left Node + total sum of right Node
    ans._sum = l._sum + r._sum
     
    # The max sum of ans Node stores the answer
    # which is the maximum value among:
    # prefix sum of ans Node
    # suffix sum of ans Node
    # maximum value of left Node
    # maximum value of right Node
    # prefix value of left Node + suffix value of right Node
    ans._max = max(ans._pre, ans._suf, ans._sum,
                    l._max, r._max, l._suf+r._pre)
 
    # Return the ans Node
    return ans
 
# Function for calculating the
# max_sum_subArray using divide and conquer
def getMaxSumSubArray(l, r, ar):
 
    if l == r: return Node(ar[l])
 
    mid = (l + r) // 2
     
    # Call method to return left Node:
    left = getMaxSumSubArray(l, mid, ar)
     
    # Call method to return right Node:
    right = getMaxSumSubArray(mid+1, r, ar)
     
    # Return the merged Node:
    return merg(left, right)
 
# Driver code
if __name__ == "__main__":
 
    ar = [-2, -5, 6, -2, -3, 1, 5, -6]
    n = len(ar)
    ans = getMaxSumSubArray(0, n-1, ar)
    print("Answer is", ans._max)
 
# This code is contributed by Rituraj Jain


C#
// C# implementation of the approach
using System;
using System.Linq;
public class GFG
{
   
class Node
{
     
    // To store the maximum sum
    // for a sub-array
    public int _max;
     
    // To store the maximum prefix
    // sum for a sub-array
    public int _pre;
     
    // To store the maximum suffix
    // sum for a sub-array
    public int _suf;
     
    // To store the total sum
    // for a sub-array
    public int _sum;
};
 
// Function to create a node
static Node getNode(int x)
{
    Node a = new Node();
    a._max = x;
    a._pre = x;
    a._suf = x;
    a._sum = x;
    return a;
}
 
// Function to merge the 2 nodes left and right
static Node merg(Node l, Node r)
{
     
    // Creating node ans
    Node ans = new Node();
 
    // Initializing all the variables:
    ans._max = ans._pre = ans._suf = ans._sum = 0;
     
    // The max prefix sum of ans Node is maximum of
    // a) max prefix sum of left Node
    // b) sum of left Node + max prefix sum of right Node
    // c) sum of left Node + sum of right Node
    ans._pre = (new int[]{l._pre, l._sum+r._pre,
                                       l._sum+r._sum}).Max();
 
    // The max suffix sum of ans Node is maximum of
    // a) max suffix sum of right Node
    // b) sum of right Node + max suffix sum of left Node
    // c) sum of left Node + sum of right Node
    ans._suf = (new int[]{r._suf, r._sum+l._suf,
                                       l._sum+r._sum}).Max();
     
    // Total sum of ans Node = total sum of
  // left Node + total sum of right Node
    ans._sum = l._sum + r._sum;
     
    // The max sum of ans Node stores
    // the answer which is the maximum value among:
    // prefix sum of ans Node
    // suffix sum of ans Node
    // maximum value of left Node
    // maximum value of right Node
    // prefix value of right Node + suffix value of left Node
    ans._max = (new int[]{ans._pre,
                                       ans._suf,
                                       ans._sum,
                                       l._max, r._max,
                                       l._suf+r._pre}).Max();
 
    // Return the ans Node
    return ans;
}
 
// Function for calculating the
// max_sum_subArray using divide and conquer
static Node getMaxSumSubArray(int l, int r, int []ar)
{
    if (l == r) return getNode(ar[l]);
    int mid = (l + r) >> 1;
     
    // Call method to return left Node:
    Node left = getMaxSumSubArray(l, mid, ar);
     
    // Call method to return right Node:
    Node right = getMaxSumSubArray(mid + 1, r, ar);
     
    // Return the merged Node:
    return merg(left, right);
}
 
// Driver code
public static void Main(String[] args)
{
    int []ar = {-2, -5, 6, -2, -3, 1, 5, -6};
    int n = ar.Length;
    Node ans = getMaxSumSubArray(0, n - 1, ar);
    Console.Write("Answer is " +  ans._max + "\n");
}
}
 
// This code is contributed by shikhasingrajput


输出:

Answer is 7

时间复杂度: getMaxSumSubArray()递归函数生成以下递归关系。
T(n)= 2 * T(n / 2)+ O(1)注意,征服部分只需要O(1)时间。因此,在使用Master定理解决此递归问题时,我们得到了O(n)的时间复杂度。