[I0A]查找最大子串问题的求解

来源:互联网 发布:tensorflow参数微调 编辑:程序博客网 时间:2024/05/08 23:29

Question:

假如你能够预知股票未来几天的动向,请你选择合适的买入和售出 日期 以便获得最大的利益值?

如下为股票未来几天的变动情况:



Result:

最优解为第 7 天买入,第11天卖出 获得最大单元利益为 43.


Analysis:

对于这个问题的求解我们当然可以用传统的排列组合方法来求解,即从以上任意天数中选择两天来进行计算

比如 选择day 3 和 day 9   101- 85 = 16 单元利益为16

所有的排列组合为 N*(N-1) / 2  时间复杂度为O(n^2)

然而此题依然可以有更效率的方式求解出结果,最终时间复杂度为O(n lg n).

如表格第三栏 change行,我们知道 要活得最大利益 即要求 第三行中 的 求得 最大子串 即 18 + 20 -7 +12 = 43

所以该问题转化为求解 最大子串问题. 


How:

如何求解最大子串问题?这里我们仅使用 分治法(Divide and Conquer)求解

分治法分为三个步骤:Divide,Conquer,Combine.

Divide 将一个大的问题划分成若干个小的同类型的问题

Conquer 对划分后的小问题进行求解

Combine 将求解后的小问题结果合并 得出最后大问题的结果

显然 如果要将这一长串数列 求出最大子串  将这长子列化分成若干个子列 然后对子列进行求解 然后将结果合并.



我们将数列分成两个子串 即A[beg] ~ A[mid] 和 A[mid+1] ~ A[end] 两个部分

假设最大子串 出现在这两个部分,我们只要求解被划分后的两个子串 


所以应该分为三部分  

1.beg<= i <= j <= mid

2.mid+1<= i <= j <= end

3.beg <= i <= mid < j <= end


首先我们针对于分界处来进行计算

result find_max_crossing_subarrary(int* arr,int beg,int mid,int end){    int left_sum =  INT_MIN,right_sum =  INT_MIN;    int left_index,right_index;    int sum = 0;    int i;    for(i = mid;i >= beg; i--){        sum = sum + arr[i];        if(sum > left_sum) {            left_sum = sum;            left_index = i;        }    }    sum = 0;    for(i = mid+1;i <= end;i++){        sum = sum + arr[i];        if(sum > right_sum) {            right_sum = sum;            right_index = i;        }    }    result res = {left_index,right_index,left_sum + right_sum};    return res;}


首先计算分界点左部分子串最大值,同时保存子串最大值时的标号index

然后计算分界点右边子串最大值,同时保存子串最大值时的标号

然后返回左右部分合并后的子串最大值

整个流程思路为计算左子串最大值,然后再计算右边子串最大值,然后从中间向两边计算交界处子串最大值 如下图所示





附上源代码

#include <stdio.h>#include <stdlib.h>#include <limits.h>typedef struct {    int beg;    int end;    int sum;} result;voidarr_print(int *arr,int len);int*arr_convertor(char **argv,int *len);int*max_difference_arr(int *arr,int len);resultfind_max_crossing_subarrary(int* arr,int beg,int mid,int end);resultfind_max_subarrary(int *arr,int beg,int end);/* day012345678910111213141516 p 100113110851051028663811019410610179949097 c 013-3-2520-3-16-231820-712-5-2215-47 */int main(int argc,char **argv){    int len;    int *arr = arr_convertor(argv,&len);    if(len < 2) {    printf("arguments must more than 2.\n");    return 0;    }    arr_print(arr,len);    int *dif_arr = max_difference_arr(arr,len);    arr_print(dif_arr,len-1);    result res = find_max_subarrary(dif_arr,0,len-2);    printf("max subarray begins at:%d ends at:%d.\n",res.beg,res.end+1);    printf("and the max sum of the subarray is %d.\n",res.sum);    printf("the max array is:");    int i = 0;    for(i = res.beg;i <= res.end+1;i++) {    printf("%4d",arr[i]);    }    free(arr);    free(dif_arr);    return 0;}result find_max_subarrary(int *arr,int beg,int end){        if(beg == end) {        result res = {beg,end,arr[beg]};        return res;    }    int mid = (beg + end)/2;    result left_res = find_max_subarrary(arr,beg,mid);    result right_res = find_max_subarrary(arr,mid+1,end);    result cross_res = find_max_crossing_subarrary(arr,beg,mid,end);    if(left_res.sum >= right_res.sum && left_res.sum > cross_res.sum)        return left_res;    else if (right_res.sum >= left_res.sum && right_res.sum >= cross_res.sum)        return right_res;    else return cross_res;    }result find_max_crossing_subarrary(int* arr,int beg,int mid,int end){    int left_sum =  INT_MIN,right_sum =  INT_MIN;    int left_index,right_index;    int sum = 0;    int i;    for(i = mid;i >= beg; i--){        sum = sum + arr[i];        if(sum > left_sum) {            left_sum = sum;            left_index = i;        }    }    sum = 0;    for(i = mid+1;i <= end;i++){        sum = sum + arr[i];        if(sum > right_sum) {            right_sum = sum;            right_index = i;        }    }    result res = {left_index,right_index,left_sum + right_sum};    return res;}int* max_difference_arr(int *arr,int len){    int* dif_arr = malloc(sizeof(int) * (len-1));    int i;    for(i = 0; i <len-1;i++){        dif_arr[i] = arr[i+1] - arr[i];    }    return dif_arr;}int* arr_convertor(char **argv,int *len){    int i;    char **ptr = argv;    int tmp[100];    while (*++ptr != NULL) {        tmp[i++] = atoi(*ptr);    }    *len = i;    int *parr = malloc(sizeof(int)*i);    for (i = 0; i < *len; i++) {        parr[i] = tmp[i];    }    return parr;}void arr_print(int *arr,int len){    int i;    for(i = 0;i < len;i++) printf("%4d",arr[i]);    printf("\n");}


测试结果如下图:



1 0