算法之最大子数组问题
2015-05-23 17:28
190 查看
最大子数组问题就是在一个数组中寻找出它的最大的非空连续子数组。本次我将采用3种方式来解决此问题。
伪代码:
C语言的实现:
伪代码:
C语言实现:
从数组左边界开始,由左到右处理,记录到目前为止已处理过的最大子数组。若已知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]的最大子数组。
C语言代码示例:
方法一:暴力求解方法
暴力方法就是简单的找出出每种可能组合出的组合,找出其中的最大的就可以了。但时间复杂度为n的平方,效率很低。伪代码:
FIND-MAX-SUBARRAY(A, low, high) left = 0 right = 0 sum = -∞ for i = low to high current-sum = 0 for j = i to high current-sum += A[j] if sum < current-sum sum = current-sum left = i right = j return (left, right, sum)
C语言的实现:
int find_max_subArray(int A[], int low, int high) { int left = 0; //最大子数组的左边界 int right = 0; //最大子数组的右边界 int sum = MINSUM; //数组大小 int current_sum; //一个用于记录当前数组大小的变量 int i, j; for(i=low;i<=high;i++) { current_sum = 0; for(j=i;j<=high;j++) { current_sum += A[j]; if(sum<current_sum) { sum = current_sum ; left = i; right = j; } } } return sum; }
方法二:分治法分解递归求解
我们要寻找子数组A[low...high]的最大子数组。采用分治法我们就需将子数组分为2个规模尽量相等的子数组。找到子数组的中间位置mid,然后就考虑求解2个子数组A[low...mid]和A[mid+1...high]。此时A[low...high]子数组中的最大连续子数组将是完全存在于A[low...mid]或者A[mid+1...high]或者跨越了重点mid的所有最大子数组中的最大者。我们可以递归的求解A[low...mid]和A[mid+1...high]的最大子数组,因为这两个仍然是最大子数组问题,只是规模更小。因此剩下的全部工作就是寻找跨越中点的最大子数组,然后在三种情况中选取和最大者伪代码:
FIND-MAX-CROSSING-SUBARRAY(A, low, mid, high) left = -∞ sum = 0 for i = mid downto low sum = sum + A[i] if sum > left-sum left-sum = sum max-left = i right-sum = -∞ sum = 0; for j = mid + 1 to high sum = sum + A[j] if sum >right-sum right-sum = sum max-right = j return (max-left, max-right, left-sum+right-sum) FIND-MAXIMUM-SUBARRAY(A, low, high) if high == low return (low, high, A[low]) else mid = (low + high) / 2 (left-low, left-hight, left-sum) = FIND-MAXIMUM-SUBARRAY(A, low, mid) (right-low, right-hight, right-sum) = FIND-MAXIMUM-SUBARRAY(A, mid+1, high) (cross-low, cross-hight, cross-sum) = FIND-MAX-CROSSING-SUBARRAY(A, low, mid, high) if left-sum >= right-sum and right-sum >= cross-sum return (left-low, left-high, left-sum) else if right-sum >= left-sum and right-sum >= cross-sum return (right-low, right-high, right-sum) else return (cross-low, cross-high, cross-sum)
C语言实现:
#include <stdio.h> #include <stdlib.h> #define MINSUM -1000; int find_max_crossing_subArray(int A[], int low, int mid, int high); int find_max_subArray(int A[], int low, int high); int max_left,max_right; int left,right; int main() { int A[] = {1,-1,3,4,6,-5,4, 9}; printf("max_sum:%d\n", find_max_subArray(A, 0, 7)); printf("left:%d\nright:%d", left, right); return 0; } int find_max_crossing_subArray(int A[], int low, int mid, int high) { int left_sum,right_sum; left_sum = MINSUM; int sum = 0; int i; for(i = mid; i >=low; i--) { sum = sum + A[i]; if(sum > left_sum) { left_sum = sum; max_left = i; } } right_sum = MINSUM; sum = 0; int j; for(j=mid+1; j<=high; j++) { sum = sum + A[j]; if(sum > right_sum) { right_sum = sum; max_right = j; } } return left_sum + right_sum; } int find_max_subArray(int A[], int low, int high) { int left_sum,right_sum,cross_sum; if(low == high) { left = low; right = high; return A[low]; } else { int mid = (low + high) / 2; left_sum = find_max_subArray(A, low, mid); right_sum = find_max_subArray(A, mid+1, high); cross_sum = find_max_crossing_subArray(A, low, mid, high); if(left_sum >= right_sum && left_sum >= cross_sum) { left = low; right = mid; return left_sum; } else if(right_sum >= left_sum && right_sum >= cross_sum) { left = mid + 1; right = high; return right_sum; } else { left = max_left; right = max_right; return cross_sum; } } }
3.线性方法
对于此个问题,有一种达到线性时间的一种算法,如下:从数组左边界开始,由左到右处理,记录到目前为止已处理过的最大子数组。若已知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]的最大子数组。
C语言代码示例:
#include <stdio.h> #include <stdlib.h> #define MINSUM -1000; typedef struct { unsigned left; unsigned right; int sum; } max_subarray; max_subarray find_maximum_subarray(int A[], unsigned low, unsigned high) { max_subarray suffixes[high - low]; suffixes[0].left = low; suffixes[0].right = low + 1; suffixes[0].sum = A[low]; int i; for ( i = low + 1; i < high; i++) { if (suffixes[i - 1].sum < 0) { suffixes[i].left = i; suffixes[i].right = i + 1; suffixes[i].sum = A[i]; } else { max_subarray *previous = &suffixes[i - 1]; suffixes[i].left = previous->left; suffixes[i].right = i + 1; suffixes[i].sum = previous->sum + A[i]; } } max_subarray *max = &suffixes[0]; for (i = low + 1; i < high; i++) { if (max->sum < suffixes[i].sum) { max = &suffixes[i]; } } return *max; } int main() { max_subarray ms; int A[] = {1,-4,3,4,6,-5,4, 9}; ms = find_maximum_subarray(A, 0, 7); printf("max_sum:%d\n", ms.sum); printf("left:%d\nright:%d", ms.left, ms.right); return 0; }
相关文章推荐
- 第四章 分治策略 4.1 最大子数组问题 (暴力求解算法)
- 【软件工程】代码复审与子数组最大和线性算法寻找问题
- JAVA代码—算法基础:最大连续子数组乘积问题
- 算法导论:分治策略__最大子数组问题
- JAVA代码—算法基础:子数组的最大累加和问题
- 【算法导论学习-007】最大子数组和问题(Maximum subarray problem)
- [算法导论]练习4.1-5最大连续子数组问题
- 最大子数组问题---线性时间算法(转)
- 4.1最大数组问题(算法导论)
- 【算法】子数组的最大累加和/子矩阵的最大累加和问题
- 经典算法——连续子数组最大和问题
- 算法导论 4.1-5 最大子数组问题 线性时间算法 C++简单实现
- 【算法设计-分治】最大子数组问题
- 最大子数组问题(动态规划)--【算法导论】
- 算法(一)最大子数组问题
- 算法导论之最大子数组问题 C语言实现
- 最大子数组问题的分治求解算法
- 【算法导论学习笔记】最大子数组问题
- 最大子数组问题(动态规划)--【算法导论】
- Leetcode121.+Leetcode53. Kadane算法解决最大子数组问题