1 简论
当一个函数用它自己来定义时就称为递归的。
要素:
- 基准情况:初始值,不用递归就能求解。
- 递归推进:朝着产生基准情形的方向推进。
- 特殊情况:函数内部对输入参数定界。
- 设计法则:假设所有递归调用都能运行。
- 合成效益法则:求解同一问题同一实例,不能做重复性工作。
2 实例
2.1 最大和连续子序列
给定一个整数序列,a0, a1, a2, …… , an(项可以为负数),求其中最大的子序列和。如果所有整数都是负数,那么最大子序列和为0;
用到了递归,用到了这样一种思想,当然要我肯定想不到,纯属借鉴了:
对于一数字序列,其最大连续子序列和对应的子序列可能出现在三个地方。或是整个出现在输入数据的前半部(左),或是整个出现在输入数据的后半部(右),或是跨越输入数据的中部从而占据左右两半部分。前两种情况可以通过递归求解,第三种情况可以通过求出前半部分的最大和(包含前半部分的最后一个元素)以及后半部分的最大和(包含后半部分的第一个元素)而得到,然后将这两个和加在一起即可。
主要从递归的角度分析,把问题分为了小问题【分治法的策略】,处理的过程都一样,然后递归。—过程和终点。
首先分析终点,子问题一步步被划分,最终,明显看出,原始序列最终会被分为一个长度为1的子序列(一个大于1的数一直除,肯定会到1的),把长度为1的子序列当成原问题,那么根据题意,如果是正数,就要,如果是负数,不如不要,就为0。所以,递归的终点设置得正确。
然后分析过程,分析递归的过程,在debug的过程中,你能发现,递归最开始是一直拆分子问题,直到到达递归终点再一层一层倒着返回调用结果。可以把递归过程看成一个二叉树,所以递归终点就是此递归过程二叉树的最后一层,而在以下程序中, MaxLeftSum = MaxSubSum(A,Left,Center); 这句就是左孩子,MaxRightSum = MaxSubSum(A,Center+1,Right);就是右孩子。而左孩子语句先执行,所以过程最开始一定是往左下方向直到第一个终点,再把他们左右序列最大值(因为两序列各只有一个,且这里必须包含左序列最右和右序列最左,所以这里就是这俩序列自身)加起来。
int MaxSubSum( int A[], int Left, int Right)
{
int MaxLeftSum,MaxRightSum;
int MaxLeftBorderSum,MaxRightBorderSum;
int LeftBorderSum,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);
MaxRightSum = MaxSubSum(A,Center+1,Right);
MaxLeftBorderSum = 0;
LeftBorderSum = 0;
for(i = Center;i >= Left;i--)
{
LeftBorderSum += A[i];
if(LeftBorderSum > MaxLeftBorderSum)
MaxLeftBorderSum = LeftBorderSum;
}
MaxRightBorderSum = 0;
RightBorderSum = 0;
for(i = Center+1;i <= Right;i++)
{
RightBorderSum += A[i];
if(RightBorderSum > MaxRightBorderSum)
MaxRightBorderSum = RightBorderSum;
}
return Max(MaxLeftSum,MaxRightSum,MaxLeftBorderSum + MaxRightBorderSum);
}
static int Max(int a, int b, int c)
{
if(a>b&&a>c)
return a;
else if(b>a&&b>c)
return b;
else
return c;
}