函数调用及返回
每次函数调用是一个压入栈的过程,当函数运行结束后,会返回到上一个函数中,弹栈。
举例:
调用情况:main()调用A(),A()调用B(),B()调用C() ---> 递进至边界
返回情况:C()执行完回到B(),B()执行完回到A(),A()执行完回到main() ---> 从边界回归到原问题
- 递归结束条件,递归边界,递归出口
- 递归调用公式,递归链条,对原问题进行划分时,对子问题和原问题处理的逻辑相似性
一些理解
将原问题划归为子问题,问题规模缩减,但子问题形式与原问题一样,这个子问题通常是通往边界的极限情况。
当还没碰到边界条件时,递归不断通向边界情况。(递归不断推进至边界)
当碰到边界条件时,递归不断返回,问题逐一解决,最后回到原问题。(从边界不断回归至原问题)
一些例子
函数里只有一条递归语句
这种不管是执行过程还是思路都比较好理解
// 输出 0~n
void f(int n){
if(n>0){ // 一直 递进到 边界 f(0)
f(n-1); // 然后 不断回归 到 f(n)
}
cout<<n<<endl;
}
// 数组 从 begin下标 求累加和
int f(int a[],int begin){
if(begin==a.length)return 0; // 一直递进到 begin == a.length
return f(a,begin+1)+a[begin];
}
即 对于 f(a,begin) --> f(a,begin+1)+a[begin]
对于 f(a,begin+1) --> f(a,begin+2)+a[begin+1]
对于 f(a,length-1) --> f(a,length)+a[length-1]
f(a,length) --> 0 出口
采用这样一种思想去递归, 并找到递归边界(出口) , 就能很好解决问题。
一样的思路:
// 求和 至 a [end]
int f(int a[],int end){
if(end==-1)return 0;
return f(a,end-1)+a[end];
}
判断字符串是否相等:
bool f(String s1,String s2){
if(s1.length!=s2.length)return false;
if(s1.length==0)return true;
if(s1.charAt(0)!=s2.charAt(0))return false; // 看 首字母 是否相等
return f(s1.substring(1),s2.substring(1)); // 递归 判断子串
}
函数里有多条递归语句时
二分的思路:
//sum(begin下标 ~ end下标),二分求和
int f(int a[],int begin,int end){
if(begin==end)return a[begin]; // 出口
int x=f(a,begin,(begin+end)/2)+f(a,(begin+end)/2+1,end);
return x;
}
不断削减 问题规模 直至 递归边界 (确定是一定可以到达边界的)
底层问题解决掉,不断回归,问题逐层得到解决。
以f(a,0,3)
为例,探究其内部过程,结果来自 Dev -C++ 调试
从数组区间 A [begin,end)
中找出最大的两个整数A [x1] 和 A [x2],A[x1] ≥ A[x2]
元素比较的次数,尽可能地少
非递归方法:
void max2(int a[],int begin,int end,int &x1,int &x2){
if(a[x1=begin]<a[x2=begin+1])swap(x1,x2); // 初始化 x1 x2
for(int i=begin+2;i<end;i++){ // 从 begin+2 处 遍历数组
if(a[x2]<a[i]){ // 如果 a[i] 比 a[x2] 还小, 就直接跳过
if(a[x1]<a[x2=i]){
swap(x1,x2);
}
}
}
}
递归方法:
void max3(int a[],int begin,int end,int &x1,int &x2){
if(begin+2==end){ // [begin,end) 区间里只有两个元素
if(a[x1=begin]<a[x2=begin+1])swap(x1,x2);
return;
}
if(begin+3==end){ // [begin,end) 区间里只有三个元素
if(a[x1=begin]<a[x2=begin+1])swap(x1,x2);
if(a[x2]<a[begin+2]){
if(a[x1]<a[x2=begin+2]){
swap(x1,x2);
}
}
return;
}
int x1l,x2l;max3(a,begin,(begin+end)/2,x1l,x2l); // 得到左边区间的最大的两个整数 a[x1l] >= a[x2l]
int x1r,x2r;max3(a,(begin+end)/2,end,x1r,x2r); // 得到右边区间的最大的两个整数 a[x1r] >= a[x2r]
if(a[x1l]>a[x1r]){
x1=x1l;x2=(a[x2l]>a[x1r])?x2l:x1r;
}else{
x1=x1r;x2=(a[x1l]>a[x2r])?x1l:x2r;
}
}
int main(){
int a[]={57,4,3,56,78};
int x1,x2;
int t1,t2;
max2(a,0,5,x1,x2);
max3(a,0,5,t1,t2);
cout<<x1<<" max2 "<<x2<<endl;
cout<<t1<<" max3 "<<t2<<endl;
}
需要注意的地方
- 递归边界的严谨性,正确性
- 确保所有情况最后都要是归结到递归边界,出口上来。
- 注意 递归逻辑,以及边界出口(类比多股诺米牌,归纳演绎 )