求解最大子数组问题

明日复明日,明日何其多。我生待明日,万事成蹉跎。


求解最大子数组的问题一般会有类似这样的描述:给定一个数组,求出数组中一个连续的子数组,该子数组中元素的和为所有连续子数组中的最大值。

这样的问题是典型分治思想,适合使用递归处理。我们可以考虑将给定的数组分成两部分,那么最大子数组必定存在于左侧部分,或者右侧部分,或者是跨越了分界线的部分。那么,只要分别求出这三种情况下的最大子数组,再进行比较,就能得出最后的结果。从这里的分析可以看出,我们采用了分解数组(分治思想),继而对每次的分解结果进行同样的处理(递归),最后求解(合并)。


C++实现示例:

int main()
{
    int A[] = {13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7};

    int low = 0;
    int high = 0;
    int sum = 0;

    find_max_subarray(A, 0, 15, low, high, sum);

    cout << "{";
    for (int i = low; i <= high; i++)
    {
        cout << A[i] << ", ";
    }

    cout << "(sum = " << sum << ")" << "}" << endl;

    system("pause");
    return 0;
}

void find_max_subarray(int* arr, int low, int high, 
                       int& result_low, int& result_high, int& result_sum)
{
    if (low == high)
    {
        result_low = low;
        result_high = high;
        result_sum = arr[low];
        return;
    }

    int mid = (low + high) / 2;

    int left_low = 0;
    int left_high = 0;
    int left_sum = 0;
    find_max_subarray(arr, low, mid, left_low, left_high, left_sum);

    int right_low = 0;
    int right_high = 0;
    int right_sum = 0;
    find_max_subarray(arr, mid + 1, high, right_low, right_high, right_sum);

    int cross_low = 0;
    int cross_high = 0;
    int cross_sum = 0;
    find_max_crossing_subarray(arr, low, mid, high, cross_low, cross_high, cross_sum);

    if (left_sum >= right_sum && left_sum >= cross_sum)
    {
        result_low = left_low;
        result_high = left_high;
        result_sum = left_sum;
    }
    else if (right_sum >= left_sum && right_sum >= cross_sum)
    {
        result_low = right_low;
        result_high = right_high;
        result_sum = right_sum;
    }
    else
    {
        result_low = cross_low;
        result_high = cross_high;
        result_sum = cross_sum;
    }
}

void find_max_crossing_subarray(int* arr, int low, int mid, int high, 
                                int& cross_low, int& cross_high, int& cross_sum)
{
    int sum = 0;
    int left_sum = arr[mid] - 1;

    for (int i = mid; i >= low; i--)
    {
        sum += arr[i];
        if (sum >= left_sum)
        {
            left_sum = sum;
            cross_low = i;
        }
    }

    sum = 0;
    int right_sum = arr[mid + 1] - 1;
    for (int i = mid + 1; i <= high; i++)
    {
        sum += arr[i];
        if (sum >= right_sum)
        {
            right_sum = sum;
            cross_high = i;
        }
    }

    cross_sum = left_sum + right_sum;
}

Java实现示例:
public static void main(String[] args) {
    int A[] = { 13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7 };
    int[] result = new int[3];
    findMaxSubarray(A, 0, 15, result);

    // output
    for (int i = result[0]; i <= result[1]; i++) {
        System.out.print(A[i]);
        System.out.print(", ");
    }

    System.out.println("Sum = " + result[2]);
}

public static void findMaxSubarray(int[] arr, int low, int high, int[] result) {
    if (low == high) {
        result[0] = low; // the left index of the array
        result[1] = high; // the right index of the array
        result[2] = arr[low]; // the sum result of the sub-array
        return;
    }

    int mid = (low + high) / 2;

    // index 0 - low index of the sub-array
    // index 1 - high index of the sub-array
    // index 2 - sum of the sub-array
    int[] leftResult = new int[3];
    findMaxSubarray(arr, low, mid, leftResult);

    int[] rightResult = new int[3];
    findMaxSubarray(arr, mid + 1, high, rightResult);

    int[] crossResult = new int[3];
    findMaxCrossingSubarray(arr, low, mid, high, crossResult);

    if (leftResult[2] >= rightResult[2] && leftResult[2] >= crossResult[2]) {
        System.arraycopy(leftResult, 0, result, 0, 3);
    }
    else if (rightResult[2] >= leftResult[2] && rightResult[2] >= crossResult[2]) {
        System.arraycopy(rightResult, 0, result, 0, 3);
    }
    else {
        System.arraycopy(crossResult, 0, result, 0, 3);
    }
}

public static void findMaxCrossingSubarray(int[] arr, int low, int mid, int high, int[] result) {
    int sum = 0;
    int left_sum = arr[mid] - 1;

    for (int i = mid; i >= low; i--) {
        sum += arr[i];
        if (sum > left_sum) {
            left_sum = sum;
            result[0] = i; // low index of the sub-array
        }
    }

    sum = 0;
    int right_sum = arr[mid + 1] - 1;
    for (int i = mid + 1; i <= high; i++) {
        sum += arr[i];
        if (sum > right_sum) {
            right_sum = sum;
            result[1] = i; // high index of the sub-array
        }
    }

    result[2] = left_sum + right_sum;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值