从方法论意义上说,递归方法是一种从简单到复杂、从低级到高级的可连续操作的解决问题的方法。它的每一步骤都是可操作的,各步骤之间是连续转换的。递归定义是用简单的、自明的要素描述、构造、说明复杂的整体。递归方法是通过解决简单的问题来解决复杂的问题。在人们的思维过程中,普遍存在着递归机制。递归方法是一种处理问题的精致技巧、解决问题的有效方法。从哲学方法论角度研究递归方法,具有重要的意义。
递归的基本原理: 1 每一次函数调用都会有一次返回.当程序流执行到某一级递归的结尾处时,它会转移到前一级递归继续执行. 2 递归函数中,位于递归调用前的语句和各级被调函数具有相同的顺序.如打印语句 #1 位于递归调用语句前,它按照递归调用的顺序被执行了 4 次. 3 每一级的函数调用都有自己的局部变量. 4 递归函数中,位于递归调用语句后的语句的执行顺序和各个被调用函数的顺序相反.即位于递归函数入口前的语句,右外往里执行;位于递归函数入口后面的语句,由里往外执行。 5 虽然每一级递归有自己的变量,但是函数代码并不会得到复制. 6 递归函数中必须包含可以终止递归调用的语句.
递归问题注意事项:
递归的关键是发现问题的相似性,或者是通过某种方法或者是手段构造相似性,在或者通过添加参数或者是模仿数学中递推公式来解决。除相似性问题外还需要确定递归的出口,否则会造成栈溢出等问题。
递归问题(一):
按顺序打印0~9,要求用两种不同的递归顺序打印
//双参数
void f(int n,int m )
{
if(n-m>0)
f(n,m+1);
printf("%d\n",n-m);
}
void f2(int n,int m )
{
printf("%d\n",m);
if(n>m)
f2(n,m+1);
}
//单参数实现
void f3(int n)
{
if(n>0)
f3(n-1);
printf("%d\n",n);
}
递归问题(二): 提供数组,利用递归的方法求数组从某下标开始到某下标的和 //正序相加 int add(int *a,int n,int m) { if(n<=m){ // printf("a[%d](%d)+ ",n,a[n]); return a[n]+add(a,n+1,m); } else return 0; }
//逆序相加
int add2(int *a,int n,int m){
if(n<=m)
return a[m]+add2(a,n,m-1);
return 0;
}
递归问题(三):
给定两个字符串,确定两个字符串是否相同(c++) #include <iostream>
using namespace std;
bool equel(char *st1,char *st2)
{
if(*st1!=*st2)
return false;
else{
if((*st1==*st2)&&(*st1=='\0'))
return true;
equel(st1+1,st2+1);
}
}
递归问题(四):
从n个球中,随意抽取m个球,不放回,求有多少种取法?
#include <iostream>
using namespace std;
int ass(int n,int m)
{
if(n==m)
return 1;
else if(m>n)
return 0;
else if(m>1)
{
return (ass(n-1,m-1)+ass(n-1,m));
}
else
return n;
}
递归问题(五): 全排列问题:生成n个元素全排列序列。 #include <iostream> using namespace std; void swap(char &ch,char &ch2) { char t; t = ch; ch = ch2; ch2 = t; } void rank (char *ch,int k,int length){ //k表示开始操作的位置 if(k==length){ for(int i=0;i<length;i++) cout<<ch[i]<<" "; cout<<endl; } for(int i=k;i<length;i++){ swap(ch[k],ch[i]);//递归 rank(ch,k+1,length); swap(ch[k],ch[i]);//回溯 } }
递归问题(五): 最大公共子序列问题。(下面的程序不适合求解大规模字符串的求解) #include <iostream> using namespace std; int max(int a,int b) { return (a >= b ? a : b); } int MaxOrder(char *st, char *st2){ if(*st=='\0'||*st2=='\0') { return 0; } else if(*st==*st2) return MaxOrder(st+1,st2+1)+1; else return (max(MaxOrder(st,st2+1),MaxOrder(st+1,st2))); }
竞赛真题:(之后更新)
真题一:(这个问题用C语言做起来相对复杂,因为有许多函数需要自己写,在真题中就是一个填空题,所以java 语言来写相对划算一点,java中提供了许多现成的方法供调用)
public class A{ public static String f(String str){ if(str.length()<=1) return str; return f(str.substring(1))+str.charAt(0); } public static void main(String[] args){ System.out.println(f("abcd")); } }
真题二:
/* 打印指定行数得杨辉三角 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 */ #include<iostream> using namespace std; //其中n表示层数,m表示位数 int f(int n,int m) { if(n==1) return 1; if(m==n||m==1) return 1; return f(n-1,m-1)+f(n-1,m); } int main() { int n,m; cin >>n; for(int i=1;i<=n;i++){ cout<<f(n,i)<<" "; } cout<<endl; return 0; }
真题三:
/* n个A,m个B进行全排列,有多少种不同顺序 */ int rang(int n,int m) { if(n==0||m==0) return 1; return rang(n-1,m)+rang(n,m-1); }
真题四:
/* 6 5+1 4+2,4+1+1 3+3,3+2+1,3+1+1+1 2+2+2,2+2+1+1,2+1+1+1+1 1+1+1+1+1+1 对于给定的正整数n,编写算法打印所有划分 */ //对整数n进行划分,用数组作为暂时存储,起到缓冲区的作用,用m来标记当前位置 void f(int n,int *arr,int m) { if(n<=0){ for(int i=0;i<m;i++){ cout<<arr[i]; if(i<m-1) cout<<"+"; } cout<<" , "; if(arr[1]==1||m==1) cout<<endl<<endl; return ; } for(int i=n;i>0;i--) { if(m>0 && i>arr[m-1]) continue; arr[m]=i; f(n-i,arr,m+1); } }
真题五:
/* 某财务部门结账时发现总金额数目不对,很可能是从明细上漏掉一笔或者是几笔, 如果已经知道明细账目清单,能通过编程找到漏掉的是哪一笔或者是那几笔嘛? 如果有多种可能,则输出所有可能的情况。 我们规定,用户输入的第一行是有错金额数。 接下来是一个整数n,表示接下来将要输入的明细账目的条数; 在接下来是n行数,分别表示每笔帐目的金额 要求程序输出:所有可能漏掉金额的组合,每个情况1行,金额按照从小到大排列, 中间用空格分开 比如: 用户输入: 6 5 3 2 4 3 1 表示有错金额是6,明细共有5笔 此时程序应该输出: 1,3,3 1,2,4 3,4 */
//这个题目的输出并没有按照题目要求的输出,但思想是一样的,做轻微改动就好
#include<iostream> using namespace std; //err_sum :有错的和 //arr :明细 //k :当前处理的位置 //cur_sum:前边的元素的累加和 //b 记录取舍 void f(int err_sum,int *arr,int k,int cur_sum,bool *b,int length) { if(cur_sum>err_sum) return ; if(err_sum == cur_sum){ for(int i=0;i<length;i++) if(b[i]==false) cout<<arr[i]<<" "; cout<<endl; return; } if(k >= length) return; b[k] = false; f(err_sum,arr,k+1,cur_sum,b,length); b[k] = true; cur_sum += arr[k]; f(err_sum,arr,k+1,cur_sum,b,length); b[k]=false;//回溯归位 }
如有问题敬请斧正。。。