算法学习笔记----最大子数组问题

题目:给定义数组A,长度为n,找出数组A中的最大子数组,例如数组A={-23,18,20,-7,12},则最大子数组为{18,20,-7,12}。

解题思路:

  ①很容易想到的方案是简单地尝试每对可能的组合,然后从这些组合中找出最大的子数组。从数组中选择一个数A(i),然后计算以A(i)开始的所有子数组的和,计算的次数为(n-i),选择的次数为n,因此该算法的时间复杂度为Θ(n^2),该算法不可取。

  ②使用分治策略来求解。假设要寻找数组A[low,high]的最大子数组,将数组分为规模相同的两部分,中间的位置假设为mid。数组A所有的连续子数组A[i..j]所处的位置必然是以下三种情况之一:

      a. 完全位于子数组A[low,mid]中,因此low≤i≤j≤mid

      b.完全位于子数组A[mid+1,high]中,因此mid+1≤i≤j≤high

       c.跨越了中间元素,因此low≤i≤mid<j≤high。

  因此数组A的最大子数组所处的位置必然是这三种情况的一种。参考归并排序中递归思想,递归地求解A[low,mid]和A[mid+1,high]中的最大子数组,然后计算跨越中间元素的最大子数组,剩下的问题就是找出这三个最大子数组中的最大子数组。

  代码实现如下:

#include <stdio.h>
#include <string.h>

#ifndef INT_MIN
#define INT_MIN (-((int)(~0U>>1)) - 1)
#endif

struct subarray {
    int start;
    int end;
    int sum;
};
    
void find_max_cross_subarray(int *a, int low, int mid, int high, void *p)
{
    struct subarray *sa = (typeof(sa))p;
    int max_left, max_right;
    int left_sum, right_sum;
    int sum;
    int i;
    
    left_sum = INT_MIN;
    sum = 0;
    for (i = mid; i >= low; --i) {
        sum += a[i];
        
        if (sum > left_sum) {
            max_left = i;
            left_sum = sum;
        }
    }
    
    right_sum = INT_MIN;
    sum = 0;
    for (i = mid + 1; i <= high; ++i) {
        sum += a[i];
        
        if (sum > right_sum) {
            max_right = i;
            right_sum = sum;
        }
    }
    
    sa->start = max_left;
    sa->end = max_right;
    sa->sum = left_sum + right_sum;
}

void find_max_subarray(int *a, int low, int high, void *p)
{
    struct subarray *sa = (typeof(sa))p;
    struct subarray *left_sa, __left_sa;
    struct subarray *right_sa, __right_sa;
    struct subarray *cross_sa, __cross_sa;
    struct subarray *tmp;
    int mid = (low + high) / 2;
    
    if (low > high) {
        fprintf(stderr, "Invalid argument.\n");
        return;
    }
    
    memset(sa, 0, sizeof(*sa));
    
    if (high == low) {
        sa->sum = a[low];
        sa->start = sa->end = low;
        return;
    }
    
    left_sa = &__left_sa;
    right_sa = &__right_sa;
    cross_sa = &__cross_sa;
    
    find_max_subarray(a, low, mid, left_sa);
    find_max_subarray(a, mid + 1, high, right_sa);
    find_max_cross_subarray(a, low, mid, high, cross_sa);
    
    if ((left_sa->sum >= right_sa->sum) &&
        (left_sa->sum >= cross_sa->sum)) {
        tmp = left_sa;
    } else if ((right_sa->sum >= left_sa->sum) && 
        (right_sa->sum >= cross_sa->sum)) {
        tmp = right_sa;
    } else {
        tmp = cross_sa;
    }
    
    memcpy(sa, tmp, sizeof(*sa));
}

int main(void)
{
    int source[] = {13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5,  -22, 15, -4, 7};
    struct subarray sa;
    
    find_max_subarray(source, 0, sizeof(source) / sizeof(source[0]) - 1, &sa);
    printf("Max sum: %d, start: %d, end: %d.\n", sa.sum, sa.start, sa.end);
    return 0;
}

  ③《算法导论》中提出的一个解题思路,从数组的左边界开始,由左至右处理,记录到目前为止已经处理过的最大子数组。若已知A[1..j]的最大子数组基于如下性质将解扩展为A[1..j+1]的最大子数组:A[1..j+1]的最大子数组要么是A[1..j]的最大子数组,要么是某个子数组A[i..j+1](1≤i≤j+1)。在已知A[1..j]的最大子数组的情况下,可以在线性时间内找出形如A[i..j+1]的最大子数组。该算法的时间复杂度为因此该算法的时间复杂度为Θ(n)。

  代码实现如下所示:

#include <stdio.h>
#include <string.h>

struct subarray {
    int start;
    int end;
    int sum;
};

#define max(__x, __y) ((__x) > (__y) ? (__x) : (__y))

static void max_sumarray(int *a, int len, void *p)
{
    struct subarray *sa = (typeof(sa))p;
    int i;
    int max_sum, prev, tmp;
    int start, end;
    
    if (!sa || (len <= 0)) {
        fprintf(stderr, "Invalid argument.\n");
        return;
    }
    
    memset(sa, 0, sizeof(*sa));
    
    max_sum = a[0];
    prev = a[0];
    start = end = 0;
    for (i = 1; i < len; ++i) {
        prev = max(a[i], prev + a[i]);
        
        if (prev < max_sum) {
            if (prev == a[i]) {
                start = i;
            }
            continue;
        }
        
        max_sum = prev;
        
        if (prev == a[i]) {
            sa->start = sa->end = i;
        } else {
            sa->start = start;
            sa->end = i;
        }
    }
    
    sa->sum = max_sum;
}

int main(void)
{
    int source[] = {13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5,  -22, 15, -4, 7};
    struct subarray sa;
    
    max_sumarray(source, sizeof(source) / sizeof(source[0]), &sa);
    printf("Max sum: %d, start: %d, end: %d.\n", sa.sum, sa.start, sa.end);
    return 0;
}


你可能感兴趣的:(算法学习笔记----最大子数组问题)