***
标题:关于分治递归求最大连续字串的具体实现
目录)
前言
``在我学习数据结构与算法(c版本)这本书中,我遇见一个棘手的问题,--数组中求连续最大子序列和--,相信不少人在leetcode中遇见或做过,书中给出的算法是分治递归,代码本身简洁明了,但背后的底层逻辑曾令我苦恼许久,查了很多资料都是介绍其思想,而其中最关键的递归细节却没有谈到,我直到近日才有点眉目,因此也想写篇博客来巩固一下。
``
一、简单回顾问题-分治思想
话说有这么一个数组A**[ ] = {4, -3, 5, -2, -1, 2, 6, -2};** 我们需要利用一个算法求出其中一个最大字序列之和,要求是连续且不能为0。****
如果我们一个个推算可知结果为11=( A[0]到A[6] ), 但如何用程序写出来尼?
于是我们给出分治递归算法,简而言之就是将上面的8个数先一分为二,得到一组数为{4,-3,5,-2},另一组为{-1,2,6,-2};之后继续一分为二直到不可再分-即得到单个数字,这样做有什么用呢? 我们拿其中一组{4,-3}来举例,所谓最大子序列无非在三个位置:1.都在左边 4 ; 2.都在右边(-3);3.中间-1(4 + (-3));
于是乎我们将这三个位置的数进行比较大小,最大的那个子序列就是我们想要的,然后我们依次将情况往前推算,得到整个数组的最大连续子序列,当然对于单个数来说其本身就是最大子序列。
二、分治的实现
我们想要实现分治的思想就不得不借助递归语句,遗憾的是书中的程序递归是如此复杂,递归深度如此之深让人望而生畏。在经过许久的思考与调试之后我完整的过了一遍细节,下面我先将书中程序放在下面让大家思考
`
#include<stdio.h>
int Max3(int a, int b, int c)
{
if (a > b && a > c)
return a;
else if (b > a && b > c)
return b;
else if (a == c && a > b)
return a;
else
return c;
}
static int MaxSubSum(const int A[], int Left, int Right )
{
int MaxLeftSum, MaxRightSum;
int MaxLeftBorderSum, MaxRightBorderSum;
int LeftBoardSum, RightBorderSum;
int Center, i;
if (Left == Right)
if (A[Left] > 0)
return A[Left];
else
return 0;
Center = (Left + Right) / 2;
MaxLeftSum = MaxSubSum(A, Left, Center); // 递归关键1 ,左边
MaxRightSum = MaxSubSum(A, Center + 1, Right);// 递归关键2 , 右边
MaxLeftBorderSum = 0; LeftBoardSum = 0; // 3
for (i = Center; i >= Left; i--)
{
LeftBoardSum += A[i];
if (LeftBoardSum > MaxLeftBorderSum)
MaxLeftBorderSum = LeftBoardSum;
}
MaxRightBorderSum = 0; RightBorderSum = 0;
for (i = Center + 1; i <= Right; i++)
{
RightBorderSum += A[i];
if (RightBorderSum > MaxRightBorderSum)
MaxRightBorderSum = RightBorderSum;
} //4 从3到4是为得到中间子序列和
return Max3(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum );
} //比较这三个位置的大小
int main()
{
int A[8] = { 4, -3, 5, -2, -1, 2, 6, -2 };
printf( "%d", MaxSubSum(A, 0, 7) );
return 0;
}
在主函数中我们第一次调用函数然后进入函数MaxSubSum中然后走到关键1这一步进行递归直到遇见基准情形,这些递归是容易理解的,因为他们都属于自已调用自已的例程,如图
另外需要注意的是,当递归返回上一级的时候,若此时下面还有其他代码时,则会先执行下面代码随后再返回上一级
例**如递归深度为3的函数返回上一级,并将返回的数赋值给深度为2函数中的MaxLeftSum,然后继续执行下面的代码。请注意:此时递归深度为2且又将执行关键2的递归嵌套,这将带来令人恐惧的逻辑!
由于篇幅原因,两幅图的步骤可能不一致,相差一步或两步。
剩下过程请读者自行推算,因为如果不自已推演几遍,是无法真正理解的。不过我仍将最后的完整途径给出来以便大家对照
(请读者观察一下每列相似的地方,这会有助你对程序的理解)
值得注意的是:你需要明白递归返回上一级是在哪个地方,因为可能下一步可能又是一个递归。
总结:细心的朋友可能会发现这里的递归不像我们初学的那样是线性的,换句话说不像图上最上面那一行一路走到底然后一路返回上一级,因为这不是一个尾递归而且这两个递归会相互嵌套!
现在我们来剖析一下程序的本质:被一直平分直到无法再分的每个子序列都需要确定两个数,一个LeftSubSum,一个RightSubSum;(单独的一个数为它本身,所以你会发现无论递归多么复杂,最终都只会返回两次结果,比如最上面的一行只会返回
LeftSubSum的结果,而往下面那么多复杂的递归目的就是为了得出RightSubSum的结果
但我无论说了多少,都比不过你自已实践一次的效果好,我能做的就是提供思路,如果读者按照我的思路一步步理解每一步的过程,你就会跟我一样惊叹这个程序与算法的神奇与美妙!!!