[算法导论-分治策略]求最大子数组之各种解法及源代码实现
2017-07-12 13:46
519 查看
/*
问题: 已知数组 A = {2,4,-1,-5,3,-4,2,-6,4,1,2,2,4,-2,2};
求: 最小子数组和
分析:
一: 分治法:把问题分解成若干个简单易解的小问题,最后再把小问题归并起来,找到我们原来要解决问题的解
本问题中,我们把问题分解成两个规模尽量相等的子问题。 A[low...mid] 和 A[mid+1...high]
那最小的子数组和长啥样呢?
1: 子数组完全位于左子数组中 A[low...mid]
2: 子数组完全位于右子数组中 A[mid+1...high]
3: 子数组介于左子数组与右子数组之间
前两种情况我们有可以归到原来的问题上,对他们再次进行分解
而最后一种就需要我们做出特殊处理
(伪代码摘自 《算法导论》)
这个是求解 情况3 的算法
Find_Max_Crossiong_Subarray(A,low,mid,high)
left_sum = -无穷 //左数组最大和
sum = 0 // 临时变量
for i = mid downto low // 第一个 for 循环找出左子数组中最大子数组和
sum = sum + A[i]
if sum > left_sum
left_sum = sum
max_left = i;
right_sum = -无穷
sum = 0;
for j = mid+1 to high // 第二个 for 循环找出右子数组中最大子数组和
sum = sum + A[j]
if sum > right_sum
right_sum = sum
max_right = j;
return (max_left, max_right, left_sum + right_sum);
Find_Maximun_subarray(A, low, high)
if high == low
return (low, high, A[low])
else mid = (low+high)/2
(left_low,left_high,left_sum) = Find_Maximun_subarray(A, low, mid)
(right_low,right_high,tight_sum) = Find_Maximun_subarray(A, mid+1, high)
(cross_low, cross_high, cross_sum) = Find_Maximun_subarray(A,low,mid,high)
if left_sum >= right_sum and left_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)
二: 扫描法
此种方法是这类问题的最优解法。时间复杂度是 O(n)。 上面的分治算法时间复杂度是 O(nlogn)
算法实现:
int FindMaxinumSubarray1(int * A, int low, int high)
{
int Sum = 0;
int temp = 0;
for (int i = low; i <= high; i++)
{
temp += A[i];
if (temp > Sum)
Sum = temp;
if (temp < 0)
temp = 0;
}
问题就是该怎么来理解这个分段:
例如: 一开始,我们另 Sum为 负无穷,这样我们可以保证至少有一个分段的总和是大于 Sum ,并会把分段中的结果temp赋值给Sum。
如 下标i=0 时, temp += A[0] , 即 temp = 2。
ps: temp 每一次更新值,都要做两次判断
1: 判断 是否要更新 Sum 的值,即子数组最大和的值
2: 判断 是否要更新 temp的值。 每当 temp 的值被累计到
负数 时, 就更新temp的值,令 temp = 0
temp 大于 此时的 Sum , 即 Sum 被更新, Sum = temp, Sum = 2 (当前最大子数组和)
temp 大于0, 继续累加
i = 1 , temp += A[1] , 即 temp = 6
temp 大于 Sum , 更新 Sum, Sum = temp , Sum = 6 (当前最大子数组和)
temp 大于 0 , temp 继续累加
.........
当 i = 5 时, A[i] = -4
此时 temp = -1 小于 0 , 即 temp 要被更新 , temp = 0
........
ps : 其他解决这类问题的算法还有 动态规划法和暴力破解法。
下面是C语言代码实现
问题: 已知数组 A = {2,4,-1,-5,3,-4,2,-6,4,1,2,2,4,-2,2};
求: 最小子数组和
分析:
一: 分治法:把问题分解成若干个简单易解的小问题,最后再把小问题归并起来,找到我们原来要解决问题的解
本问题中,我们把问题分解成两个规模尽量相等的子问题。 A[low...mid] 和 A[mid+1...high]
那最小的子数组和长啥样呢?
1: 子数组完全位于左子数组中 A[low...mid]
2: 子数组完全位于右子数组中 A[mid+1...high]
3: 子数组介于左子数组与右子数组之间
前两种情况我们有可以归到原来的问题上,对他们再次进行分解
而最后一种就需要我们做出特殊处理
(伪代码摘自 《算法导论》)
这个是求解 情况3 的算法
Find_Max_Crossiong_Subarray(A,low,mid,high)
left_sum = -无穷 //左数组最大和
sum = 0 // 临时变量
for i = mid downto low // 第一个 for 循环找出左子数组中最大子数组和
sum = sum + A[i]
if sum > left_sum
left_sum = sum
max_left = i;
right_sum = -无穷
sum = 0;
for j = mid+1 to high // 第二个 for 循环找出右子数组中最大子数组和
sum = sum + A[j]
if sum > right_sum
right_sum = sum
max_right = j;
return (max_left, max_right, left_sum + right_sum);
Find_Maximun_subarray(A, low, high)
if high == low
return (low, high, A[low])
else mid = (low+high)/2
(left_low,left_high,left_sum) = Find_Maximun_subarray(A, low, mid)
(right_low,right_high,tight_sum) = Find_Maximun_subarray(A, mid+1, high)
(cross_low, cross_high, cross_sum) = Find_Maximun_subarray(A,low,mid,high)
if left_sum >= right_sum and left_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)
二: 扫描法
此种方法是这类问题的最优解法。时间复杂度是 O(n)。 上面的分治算法时间复杂度是 O(nlogn)
算法实现:
int FindMaxinumSubarray1(int * A, int low, int high)
{
int Sum = 0;
int temp = 0;
for (int i = low; i <= high; i++)
{
temp += A[i];
if (temp > Sum)
Sum = temp;
if (temp < 0)
temp = 0;
}
问题就是该怎么来理解这个分段:
例如: 一开始,我们另 Sum为 负无穷,这样我们可以保证至少有一个分段的总和是大于 Sum ,并会把分段中的结果temp赋值给Sum。
如 下标i=0 时, temp += A[0] , 即 temp = 2。
ps: temp 每一次更新值,都要做两次判断
1: 判断 是否要更新 Sum 的值,即子数组最大和的值
2: 判断 是否要更新 temp的值。 每当 temp 的值被累计到
负数 时, 就更新temp的值,令 temp = 0
temp 大于 此时的 Sum , 即 Sum 被更新, Sum = temp, Sum = 2 (当前最大子数组和)
temp 大于0, 继续累加
i = 1 , temp += A[1] , 即 temp = 6
temp 大于 Sum , 更新 Sum, Sum = temp , Sum = 6 (当前最大子数组和)
temp 大于 0 , temp 继续累加
.........
当 i = 5 时, A[i] = -4
此时 temp = -1 小于 0 , 即 temp 要被更新 , temp = 0
........
ps : 其他解决这类问题的算法还有 动态规划法和暴力破解法。
下面是C语言代码实现
#include<stdio.h> #include<malloc.h> #include<stdlib.h> int* FindMaxCrossingSubarray(int* A, int low, int mid, int high); //寻找跨越中点的最大子数组 int* FindMaximunSubarray(int*A , int low, int high); // 寻找没有跨越中点的最大子数组 void Print(int *A,int length); //打印结果 int FindMaxinumSubarray1(int*A, int low, int high); int main() { int A[] = {2,4,-1,-5,3,-4,2,-6,4,1,2,2,4,-2,2}; int *p = A; Print(p,15); p = FindMaximunSubarray(A,0,14); for (int i = 0; i < 3; i++) { printf("%d ",*(p+i)); } printf("-----------------------------\n"); printf("%d ",FindMaxinumSubarray1(A,0,14)); system("pause"); return 0; } int * FindMaxCrossingSubarray(int * A, int low, int mid, int high) { int LeftSum = INT_MIN; int Sum = 0; int MaxLeft = mid; for (int i = mid; i >= low; i--) { Sum = Sum + *(A+i); if (Sum > LeftSum) { LeftSum = Sum; MaxLeft = i; } } int RightSum = INT_MIN; int MaxRight = mid+1; Sum = 0; for (int j = mid + 1; j <= high; j++) { Sum = Sum + *(A+j); if (Sum > RightSum) { RightSum = Sum; MaxRight = j; } } int *re = (int*)malloc(sizeof(int)*3); *re = MaxLeft; *(re + 1) = MaxRight; *(re + 2) = LeftSum + RightSum; return re; } int * FindMaximunSubarray(int *A, int low, int high) { if (low == high) { int *re = (int*)malloc(sizeof(int) * 3); *re = low; *(re + 1) = high; *(re + 2) = A[low]; return re; } else { int mid = (low + high) / 2; int *Arr1 = FindMaximunSubarray(A, low, mid); int *Arr2 = FindMaximunSubarray(A,mid+1,high); int *Arr3 = FindMaxCrossingSubarray(A, low, mid, high); if (*(Arr1 + 2) > *(Arr2 + 2) && *(Arr1 + 2) > *(Arr3 + 2)) return Arr1; else if (*(Arr2 + 2) > *(Arr1 + 2) && *(Arr2 + 2) > *(Arr3 + 2)) return Arr2; else return Arr3; } } void Print(int * A, int length) { int *q = A + length ; for (int* p = A; p < q; p++) { printf("%d ",*p); } /*for (int i = 0; i < length; i++) { printf("%d ",*(A+i)); }*/ printf("\n"); } int FindMaxinumSubarray1(int * A, int low, int high) { int Sum = 0; int temp = 0; for (int i = low; i <= high; i++) { temp += A[i]; if (temp > Sum) Sum = temp; if (temp < 0) temp = 0; } return Sum; }
相关文章推荐
- 算法导论_最大子数组问题(分治策略)
- 最大连续子数组和(算法导论算法实现)
- 算法导论--分治策略求解最大子数组问题
- 算法导论 4.1-5 最大子数组问题 线性时间算法 C++简单实现
- 算法导论4.1-3~4.1-5最大子数组暴力算法和递归算法的实现和线性时间算法实现
- 算法导论之最大子数组问题 C语言实现
- 【算法导论学习-18】queue的数组实现
- 韩顺平_PHP程序员玩转算法公开课(第一季)07_使用数组实现堆栈_学习笔记_源代码图解_PPT文档整理
- 算法导论第五章实例伪码转C++_最大子数组
- C++最大堆实现priority_queue优先级队列(算法导论)
- 求最大子数组的和,算法导论之分治递归求解,暴力求解,记忆扫描方法。
- 算法导论------------栈(stack)简单的数组实现
- 「算法导论」:分治法求最大子数组
- 求一个数组中的最大值和最小值的算法改进 php 实现
- 【算法导论】最大子数组问题
- 求两个整数的最大公约数的各种算法(C语言实现)
- 从一个数组中找出第k小元素的随机化算法 c语言实现 算法导论第九章
- 最大子数组问题(分治法)--【算法导论】
- 最大子数组问题(动态规划)--【算法导论】