算法导论-分治、最大子序列问题

来源:互联网 发布:安卓系统windows模拟器 编辑:程序博客网 时间:2024/05/16 15:59

http://blog.csdn.net/silangquan/article/details/8062229


一.基本概念
分治法的基本步骤:
1.分解问题(Divide):把原问题分解为若干个与原问题性质相类似的子问题;
2.求解字问题(Conquer):不断分解子问题并求解;
3.合并子问题的解(Combine).


分治法的运用条件:
1.原问题可以分解为若干与原问题的解;
2.子问题可以分解并可以求解;
3.子问题的解可以合并为原问题的解;
4.分解后的子问题应互相独立,即不包含重叠子问题(如菲不那切竖列)。

求解递归函数的方法:

1.代换法
1)猜测解的行为;
2)数学归纳法证明。

2.递归树法
在递归树中,每一个结点都代表递归函数调用集合中一个子问题的代价。将树中每一层内的代价相加得到一个每层代价的集合,再将每层的代价相加得到递归是所有层次的总代价。

3.主方法

主要是记忆三种情况,根据各种情况直接写出递归函数。

T(n) = aT(n/b)+h(n)
a >=1 ; b >1 ; h(n) : 不参与递归的复杂度函数
判断n^log b (a)与h(n)的大小关系
= Θ(h(n)) :该方法的复杂度为 Θ(h(n)*lg(n))
> Θ(h(n)) :该方法的复杂度为 Θ(n^(log a/log b))
< Θ(h(n)) :该方法复杂度为 Θ(h(n))
这样可以帮助你快速的分析出你得算法的复杂度是否符合要求。


二.最大子序列问题
对于一个包含负值的数字串array[1...n],要找到他的一个子串array[i...j](0<=i<=j<=n),使得在array的所有子串中,array[i...j]的和最大。
这里我们需要注意子串和子序列之间的区别。子串是指数组中连续的若干个元素,而子序列只要求各元素的顺序与其在数组中一致,而没有连续的要求。

1.暴力解法,时间复杂度O(n~2)。
i表示子序列起始下标,j表示内部循环开始下表,遍历子序列的开头和结束下标,计算子序列的和,
[cpp] view plaincopy
  1. int MaxSubSum (int a[], int n)  
  2. {  
  3.     int i, j, maxSum = 0;  
  4.     for (i = 0; i < n; i++)  
  5.     {  
  6.         int thisSum = 0;  
  7.         for (j = i; j < n; j++)  
  8.         {  
  9.             thisSum += a[j];  
  10.             if (thisSum > maxSum)  
  11.                 maxSum = thisSum;  
  12.         }  
  13.     }  
  14.     return maxSum;  
  15. }  



2.Kadane算法,时间复杂度(0(n)).
原理:将数组从左到右分割为若干子串,使得除了最后一个子串之外,其余子串的各元素之和小于0,
且对于所有子串array[i...j]和任意k(i<=k<j),有array[i...k]的和大于0。满足条件的和最大子串,

只能是上述某个子串的前缀,而不可能跨越多个子串。

原理详细可参考:http://blog.csdn.net/joylnwang/article/details/6859677

执行流程:从头到尾遍历目标数组,将数组分割为满足上述条件的子串,同时得到各子串的最大前缀和,然后比较各子串的最大前缀和,得到最终答案。

以array={-2, 1, -3, 4, -1, 2, 1, -5, 4}为例,来简单说明一下算法步骤。通过遍历,可以将数组分割为如下3个子串(-2),(1,-3),(4,-1,2,1,-5,4),
这里对于(-2)这样的情况,单独分为一组。各子串的最大前缀和为-2,1,6,所以目标串的最大子串和为6。
[cpp] view plaincopy
  1. int KadaneMax (int a[], int n)  
  2. {  
  3.     int i, j, curMax, max, curLeft, curRight;  
  4.     curMax = max = curLeft = curRight = 0;  
  5.     for (i = 0; i < n; i++)  
  6.     {  
  7.         curMax += a[i];  
  8.         if (curMax > 0)  
  9.         {  
  10.             curRight = i;  
  11.             //更新最大值  
  12.             if (max < curMax)  
  13.                 max = curMax;  
  14.         }  
  15.         //重新分割子串  
  16.         else  
  17.         {  
  18.             curMax = 0;  
  19.             curLeft = curRight = i + 1;  
  20.         }  
  21.     }  
  22.     return max;  
  23. }  


3.递归实现,时间复杂度为O(nlogn)。
本期的主角上场。

分治的思想:最大子序列和可能出现在三处。或者整个出现在输入数据的左半部,或者整个出现右半部,或者跨越输入数据的中部从而占据左右两个半部分。前两种情况递归求解。第三种情况的最大和可以通过求出前半部分的最大和(包含前半部分的最后一个元素)以及后半部分的最大和(包含后半部分的第一个元素)而得到,然后将这两个和加在一起,求出三个值的最大值。

[cpp] view plaincopy
  1. int recursionMax (int a[],int left, int right)  
  2. {  
  3.     int i,j;  
  4.     if (left == right)      //base case  
  5.         if (a[left] > 0)  
  6.             return a[left];  
  7.         else  
  8.             return 0;  
  9.   
  10.     int center = (left + right) / 2;  
  11.     // //每次递归返回时,该值为该子段的最终左最大子序列和    
  12.     int maxLeftSum = recursionMax (a, left, center);  
  13.     //每次递归返回时,该值为该子段的右最大自序列和    
  14.     int maxRightSum = recursionMax(a, center + 1, right);  
  15.     //从中间向左扩展求子段的最大子序列和,必然包括子段的最右端数    
  16.     int maxLeftBorderSum = 0, leftBorderSum = 0;  
  17.   
  18.     for (i = center; i >= left; i--)  
  19.     {  
  20.         leftBorderSum += a[i];  
  21.         if (leftBorderSum > maxLeftBorderSum)  
  22.             maxLeftBorderSum = leftBorderSum;  
  23.     }  
  24.   
  25.     int maxRightBorderSum = 0, rightBorderSum = 0;  
  26.     for (j = center + 1; j <right; j++)  
  27.     {  
  28.         rightBorderSum += a[j];  
  29.         if (rightBorderSum > maxRightBorderSum)  
  30.             maxRightBorderSum = rightBorderSum;  
  31.     }  
  32.     int  tmp=maxLeftSum>maxRightSum?maxLeftSum:maxRightSum;  
  33.     if (tmp>=maxLeftBorderSum + maxRightBorderSum) return tmp;  
  34.     else return maxLeftBorderSum + maxRightBorderSum;  
  35. }  

main函数调用:

[cpp] view plaincopy
  1. int main ()  
  2. {  
  3.       
  4.     int a[] = { -2, 1, -3, 4, -1, 2, 1, -5, 4,0};  
  5.     printf ("MaxSubSum is:%6d.\n", MaxSubSum(a, 9));  
  6.     printf ("KadaneMax is:%6d.\n", KadaneMax(a, 9));  
  7.     printf ("recursionMax is:%6d.\n", recursionMax(a,0, 9));  
  8.     return 0;  
  9. }  

运行结果:



0 0
原创粉丝点击