分治策略
步骤:
- 分解
- 解决
合并
递归情况和基本情况:求解递归式时,可以使用三种方法,a 代入法 b 递归树法 c 主方法
最大子数组
A[low..high]的任何连续子数组A[i..j]所处的位置必然是以下几个情况之一:
- 完全位于子数组A[low..mid] low<=i<=j<=mid
- 完全位于子数组A[mid..high] mid
FIND_MAX_CROSSING_SUBARRAY(A, low, mid, high)
left_sum = -无穷
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_high, left_sum) = FIND_MAXIMUM_SUBARRAY(A, low, mid)
(right_low, right_high, right_sum) = FIND_MAXIMUM_SUBARRAY(A, mid+1, high)
(cross_low, cross_right, cross_sum) = FIND_MAX_CROSSING_SUBARRAY(A, low, mid, high)
if (left_sum >= right_sum && left_sum >= cross_sum)
return (left_low, left_high, left_sum)
else if (right_sum >= left_sum && right_sum >= cross_sum)
return (right_low, right_high, right_sum)
else
return (cross_low, cross_high, cross_sum)
可以通过计算递归式发现,分治方法得到的这个最大子数组问题的时间复杂度优于暴力求解的O(n*n),得到的时间复杂度为O(n*lgn)
简单实现下:
#include <stdio.h>
#include <stdlib.h>
void result_save(int result[], int source[]);
void find_maximum_subarray(int A[], int low, int high, int result[]);
void find_max_crossing_subarray(int A[], int low, int mid, int high, int result[]);
void find_max_crossing_subarray(int A[], int low, int mid, int high, int result[]){
int max_left;
int max_right;
int left_sum = -1;
int sum = 0;
for (int i = mid; i >= low; --i) { //left
sum += A[i];
if (sum > left_sum) {
left_sum = sum;
max_left = i;
}
}
int right_sum = -1;
sum = 0;
for (int j = mid+1; j <= high; ++j) {
sum += A[j];
if (sum > right_sum) {
right_sum = sum;
max_right = j;
}
}
result[0] = max_left;
result[1] = max_right;
result[2] = left_sum + right_sum; //sum
}
void find_maximum_subarray(int A[], int low, int high, int result[]){
if (low == high) {
result[0] = low;
result[1] = high;
result[2] = A[low];
} else {
int mid = (low + high)/2;
int result1[3] = {0, 0, 0};
find_maximum_subarray(A, low, mid, result1);
int result2[3] = {0, 0, 0};
find_maximum_subarray(A, mid+1, high, result2);
int result3[3] = {0, 0, 0};
find_max_crossing_subarray(A, low, mid, high, result3);
if (result1[2] >= result2[2] && result1[2] >= result3[2])
result_save(result, result1);
else if (result2[2] >= result1[2] && result2[2] >= result3[2])
result_save(result, result2);
else
result_save(result, result3);
}
}
void result_save(int result[], int source[]){
result[0] = source[0];
result[1] = source[1];
result[2] = source[2];
}
int main(void){
int A[16] = {13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7};
int result[3] = {0, 0, 0};
find_maximum_subarray(A, 0, 15, result);
printf("the max sum is %d\n", result[2]);
printf("the maximum number array is : ");
for (int i = result[0]; i <= result[1]; ++i)
printf("%d ", A[i]);
exit(0);
}