算法之浅谈递归与分治

递归?首先明白何为递归。

递归有两个要点:1.自己直接或者间接地调用自己。2.必须有递归出口。

优点:容易设计算法,易于思考。

缺点:时间复杂度过高,运用递归时,注意数据规模。

递归特点:

1、问题P的描述涉及规模P(n);  2、规模变小之后,问题的性质不会发生变化;    3、问题有解决出口

样例分析:

一、阶乘问题    二、汉诺塔问题    三、全排列问题   

四、最大公约数和最小公倍数        五、整数划分问题


问题一解答,递归完成n!f(n)=n*f(n-1) n!=0  if(n==0) f(n)=1;

int f(int n){//求n!
    if(n==0) return 1;
    else if(n<0) return n*f(n+1);
    else return n*f(n-1);
}


问题二描述:

有X,Y,Z三个塔柱,X上有n个盘子,从小到大盘子一次增大,求出把盘子移动到Z上的最小步数。

要求:1.一次移动一个     2.任何时刻,要求大的盘子都不能在小的盘子上面。Y作为辅助塔柱


问题分析:设置函数Hanoi(X,Y,Z,n);

1,若要吧把第n个盘子移动到Z上,首先把X上的前n-1个盘子移动到Y塔柱上,(此时Z可以作为辅助塔柱)

此时函数为Honoi(X,Z,Y,n-1);  Move X----->Z(1次)

2、把Y上的全部盘子移动到Z上 ,函数为Hanoi(Y,X,Z,n-1);

int sum=0;//每次运行时,置0
void Hanoi(char X,char Y,char Z,int n){
   if(n>0){
        Hanoi(X,Z,Y,n-1);
        ++sum;
        Hanoi(Y,X,Z,n-1);
   }

}


问题三:

求数组的全排列或者求数组a的第k到第n个元素的全排列。

深度搜索,

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
void range(int a[],int k,int n){
    if(k==n) {
        for(int i=1;i<=n;i++){
            printf("%d",a[i]);
        }
        printf("\n");
    }
    for(int i=k;i<=n;i++){
        swap(a[k],a[i]);
        range(a,k+1,n);
        swap(a[k],a[i]);
    }

}
int main()
{
    int a[10]={0,1,2,3,4,5,6,7,8,9};
    range(a,1,4);
    return 0;
}

问题四:

题目描述:将一个整数n表示成一系列正整数之和。n=n1+n2+......+nk,其中5=1+4; 5=4+1是一种,

正整数n的不同划分个数为正整数的划分数,记做P(n).

1、题解,在正整数所有不同划分中,将最大加数n1不大于m的划分记做q(n,m);

(1)、q(n,1)=1; n>=1 即 n=1+1+...+1

(2)、q(n,m)=q(n,n) m>=n; 最大加数不能大于n,q(1,m)=1

(3)、q(n,n)=1+q(n,n-1) 最大加数等于n和小于n的所有个数

(4)、q(n,m)=q(n,m-1)+q(n-m,m); 最大加数等于m和小于m-1的划分个数

代码如下:

int q(int n,int m){
    if((n<1)||(m<1)) return 0;
    if((n==1)||(m==1)) return 1;
    if(n<m) return q(n,n);
    if(n==m) return q(n,m-1)+1;
    return q(n,m-1)+q(n-m,m);
}
int qq(int n){
    if(n<1) return 0;
    if(n==1) return 1;
    else return qq(n-1)+1;
}

进阶问题:求出正整数n的所有划分并打印,

例如:

n=2   :2=1+1

n=3   :3=1+2

            =1+1+1

n=4   :4=1+3

            =1+1+2

            =1+1+1+1

            =2+2

n=7   :7=1+6

            =1+1+5

            =1+1+1+4

            =1+1+1+1+3

            =1+1+1+1+1+2

            =1+1+1+1+1+1+1

            =1+1+1+2+2

            =1+1+2+3

            =1+2+4

            =1+3+3

            =2+5

            =2+2+3

            =3+4

题解:可以用数组a存储完成n的一种划分,分析n=7时,可以按a[1]分类(即以1开头,按划分的第一的数分类),可得到a[1]=[1,n/2],共n/2大类,对于每一类划分都有a[1]=i; a[2]=n-i; 然后从k=2,继续拆分a[k],a[k]能否拆分取决于a[k]/2是否大于a[k-1].递归过程中的参数,指向的a[k]的下标k

代码:

#include<iostream>
#include<cstdio>
#define MOD 1000000007
using namespace std;
void  split(int t,int n){
    printf("%d=",n);
    for(int i=1;i<=t;i++){
        if(i==1) printf("%d",a[i]);
        else printf("+%d",a[i]);
    }
    printf("\n");
    int j,l;
    j=t; l=a[j];
    for(int i=a[j-1];i<=l/2;i++){
        a[j]=i; a[j+1]=l-i;
        split(j+1,n);
    }
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n/2;i++){
        //cout<<"      "<<endl;
        a[1]=i; a[2]=n-i; split(2,n);
    }

    return 0;
}

备注:笔者水平有限,希望各路大牛不吝指教。于2014年10月7日  22:22






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/** * * @author SunnyMoon */ ////////////////////////////////////////////////////////////////////////////// /** * 概念介绍: * * 消除递归: * 一个算法作为一个递归的方法通常从概念上很容易理解,但实际使用中递归的效率不高,在这种 * 情况下,把递归算法转换成非递归算法是非常有用的,这种转换经常用到栈。 * * 递归和栈: * 递归和栈之间有着紧密的联系,大部分的编译器使用栈实现递归的。 * * 调用方法的时候发生什么: * 1. 编译器会把这个方法所有当前参数及返回地址压入栈中; * 2. 将控制权交给这个方法,方法通过获得栈顶元素值访问参数; * 3. 方法运行结束的时候,值退栈,参数消失且控制权重新回到返回地址; * * 模拟递归方法: * 可以将任意一个递归方法转换为非递归的基于栈的方法。在一些简单的情况可以完全消除栈,只 * 使用一个简单的循环,但是在很复杂的情况,算法中必须须要保留栈。本例子是简单的情况,可 * 以进一步完全消除栈。 */ ///////////////////////////////////////////////////////////////////////////// /** * 计算三角数字的问题: * 递归算法描述如下 * int triiangle(int n){ * if(n==1) * return 1; * else * return (n+triangle(n-1)); * } */ import java.io.*; /** * 模拟一个递归方法,通用的方式消除递归 */ class Params {//封装了方法的返回地址和方法的参数 public int number; public int returnAddress; public Params(int num, int returnAdd) { number = num; returnAddress = returnAdd; } } class StackX {//模拟递归时使用的栈 private int maxSize; private Params[] stackArray; private int top; public StackX(int s) { maxSize = s; stackArray = new Params[maxSize]; top = -1; } public void push(Params p) { stackArray[++top] = p; } public Params pop() { return stackArray[top--]; } public Params peek() { return stackArray[top]; } } class StackTriangleApp { static int theNumber; static int theAnswer; static StackX theStack; static int logicAddress; static Params theseParams; public static void main(String[] args) throws IOException{//主方法 System.out.print("Number = "); theNumber = getInt(); stackTriangle(); System.out.println(""); System.out.println("Trriangle = " + theAnswer); } @SuppressWarnings("empty-statement") public static void stackTriangle() {//计算三角数字的方法,模拟递归方法 theStack = new StackX(100); logicAddress = 1;//设置一个逻辑地址为入口地址 while (step() == false); } public static boolean step() { switch (logicAddress) { case 1: theseParams = new Params(theNumber, 6);//设定循环返回的地址 theStack.push(theseParams); logicAddress = 2; break; case 2: theseParams = theStack.peek(); if (theseParams.number == 1) { theAnswer = 1; logicAddress = 5; } else { logicAddress = 3; } break; case 3: Params newParams = new Params(theseParams.number - 1, 4); theStack.push(newParams); logicAddress = 2; break; case 4: theseParams = theStack.peek(); theAnswer = theAnswer + theseParams.number; logicAddress = 5; break; case 5: theseParams = theStack.peek(); logicAddress = theseParams.returnAddress; theStack.pop(); break; case 6: return true; } return false; } public static String getString() throws IOException{ InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); String s = br.readLine(); return s; } public static int getInt() throws IOException{ String s=getString(); return Integer.parseInt(s); } } /** * 总结: * 当要求效率的时候可以把弟归转化为基于栈的非递归,进而可以把基于栈的转化为仅有循环的 * 非递归,这种情况下效率是最高的。 * 但是一些复杂的情况可以转化为基于栈的非递归,但是无法消除栈的。 * 一些递归算法是非常优秀的,比如分治算法。 */

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值