2.分治策略递归学习

一.什么是分治策略?

我的总结是:大分小,小合大。
这句话将贯穿下面所有递归算法。

首先简单了解下递归:

1.用例子学习递归

1)阶乘函数

阶乘函数的定义:

当n=0时,n!=1
当n>0时,n!=n(n-1)!

从上面我们可以知道递归的边界条件为n=0,递归方程也就是n(n-1),由此,我们可以写出下面代码:

int jieCheng(int n){
    if(n==0) return 1;
    return jieCheng(n-1)*n;
}

实例展示

public class RecursionStudy {

    /**
     * 阶乘算法的递归实现
     * @param n 
     * @return
     */
    static int jieCheng(int n){
        if(n==0) return 1;
        return jieCheng(n-1)*n;
    }

    public static void main(String[] args) {
        System.out.println(jieCheng(4));
    }
}
2)斐波那契数

问题描述:给出一个数,求出它的斐波那契数
1,1,2,3,5,8,13是一个斐波那契数列,当n=5时,它的斐波那契数为8,依次类推。
其公式定义为:

n<=1时, F(n)=1
n>1时,  F(n)=F(n-1)+F(n-2)

很明显我们可以用递归的方式进行问题解决:

public int Fib(int n){
    if(n<=1) 
      return 1;
    else
      return Fib(n-1)+Fib(n-2);
}

实例展示

public class RecursionStudy {
    /**
     * 斐波那契数
     * @param n
     * @return
     */
    public static int Fib(int n){
        if(n<=1)
            return 1;
        else
            return Fib(n-1)+Fib(n-2);
    }

    public static void main(String[] args) {
        for (int i=0;i<10;i++){
            System.out.print(Fib(i)+" ");
        }
    }
}

2.解决全排列问题

1)问题描述

设计一个递归算法,生成n个元素{r1,r2,r3,r4,…,rn}的全排列。

在思考这个问题之前,我们知道了递归是在一个函数中调用自身有限嵌套来实现了。
所以我们首先可以尝试一层一层地来实现,这种方式当嵌套数很少的时候还好,但是,数量多了,这样根本行不通!
所以我们要真正理解递归,必须理解这句话:递归的关键是将其抽象成一个递推公式,不要去想一层层的调用关系,不要试图用人脑取分解递归的每个步骤,它是一种思想。

解决思路:由此,我们开始重新思考学习如何使用递归来解决这个问题
首先,我们需要得出递推公式:

R={r1,r2,r3,r4,r5,..rn}

我们需要先找出这个问题的最小子问题,其实我们可以发现,这个最小子问题其实就是两个元素的排列。

Rmin={r1,r2}的全排列为{r1,r2},{r2,r1} | Rmin={r1} 的全排列为{r1}

有了这个基础我们可以得到问题的解的递推公式:
Perm(R)为R的全排列结果集

Ri=R-{ri}

当n>1时,有:

Perm(R)=(r1)Perm(R1)+(r2)Perm(R2)+...+(rn)Perm(Rn);然后以此类推

现在我们可以用一个例子来分析验证:R={1,2,3}

Perm(R)=(1)Perm({2,3})+(2)Perm({1,3})+(3)Perm({1,2})
       ={1,2,3}+{1,3,2}+{2,1,3}+{2,3,1}+{3,1,2}+{3,2,1}

可以知道有六种情况。发现可行,下面我们用代码实现:

/**
 * 全排列问题
 * @param a
 * @param cursor
 * @param k
 */
public static void allPermutation(int[] a,int cursor,int k){
    //递归终止条件
    if(cursor==k){
        System.out.println(Arrays.toString(a));
    }
    for(int i=cursor;i<=k;i++){
        if(!equal(a,cursor,i)) continue;
        swap(a,cursor,i);
        allPermutation(a,cursor+1,k);
        swap(a,cursor,i);
    }
}
public static void swap(int[] a,int cursor,int i){
    int temp = a[cursor];
    a[cursor]=a[i];
    a[i]=temp;
}
public static boolean equal(int[] a,int cursor,int i){
    for(int j=cursor;j<i;j++){
        if(a[j]==a[i]) return false;
    }
    return true;
}
@Test
public void allPermutationTest(){
    int[] a={1,2,3};
    allPermutation(a,0,a.length-1);
}

3.解决整数划分问题

1)问题描述

将正整数n表示成一系列正整数之和:n=n1+n2+n3+…+nk;
其中n1>=n2>=n3>=n4…>=nk>=1 k>=1,正整数n的这种表示称为正整数n的划分。
求正整数n的不同划分个数

例如:正整数6有如下划分个数

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+1

从这个问题我们也能很快找到最小子问题 就是整数n=1时,它的划分为 1 ,n=2时,它的划分为 1+1
q(6)=0+q(6) + 1+q(5) + 2+q(4) + 3+q(3) 以此类推
代码如下:

public static int intDivision(int n,int m){
    if(n<1||m<1) return 0;
    if(n==1||m==1) return 1;
    if(n<m) return intDivision(n,n);
    if(n==m) return intDivision(n,m-1)+1;
    return intDivision(n,m-1) + intDivision(n-m,m);
}
@Test
public void intDivisionTest(){
    System.out.println(intDivision(6,6));
}

注意:我们不建议使用递归,在算法中,使用递归的缺点大于它的优点,这一节只是对算法有一些基本的了解,了解即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小牧之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值