分治与递归习题整理

求解n的阶乘

注意:

  • cpu代表的是时间方向 可以耗损的时间是无穷的,所以可以无限循环
    内存代表的是空间方向 内存是有限的,所以不能无限递归
  • 阶乘结果 超出int范围(4字节)怎么办?
    n是有符号数时,限制n不能大于12,或者 令溢出时为负数时为-1
    当n是无符号数时,不会变成负数,一旦出现进位、借位,CF 就为 1。

递归解法

  //   O(n)  S(n)(执行n次递归语句,开辟n次栈帧)
    public  static int fac(int n){
        if(n<=1) //退出这一层调用    如果无限递归,会 栈溢出(因为每当调用发生时,就要分配新的栈帧)
            return 1;
        else
            return fac(n-1)*n;
    }

非递归解法

// O(n)  S(1)
public static int fun(int n){
    int sum=1;//0的阶乘是1
    for(int i=1;i<=n;i++){//如果发生死循环,耗损cpu时间资源,其他程序会运行慢一点
        sum*=i;
    }
    return sum;
}

求解 Fibonacci 数列

题目:

无穷数列 1,1,2,3,5,8,13,21,34,55,…,称为 Fibonacci 数列,计算第 n 位数列

递归解法

代码
/*
                  序号:0  1   2   3   4   5   6
 Fibonacci 斐波那契数列:1  1   2   3   5   8  13
*/
//    O(2^n)(总结点数)      S(n)(树的最大高度个栈帧,然后就销毁,回退,再往下递推)
public  static  int Fib(int n){
    if(n<=1){
        return 1;
    }else{
        return Fib(n-1)+Fib(n-2);//调用形式像二叉树,层层往下计算
    }
}
复杂度分析

O(f(n)):为算法的渐进时间复杂度,简称时间复杂度。
o(小o):表示小于,是最坏时间复杂度
在这里插入图片描述

递归调用图

在这里插入图片描述

优化时间复杂度

在这里插入图片描述

 public  static  int optimize_Fib(int n,int first,int second){
       if(n<=1)  {
           return second;
       }else{
           return optimize_Fib(n-1,second,first+second);//消除了重复项的计算,利用first+second暂存,类似于下面的for循环实现
       }
    }
    public  static  int optimize_Fib(int n){
        int first=1,second=1;
        return  optimize_Fib(n,first,second);//单分支递归
    }

非递归解法

    /*
    O(n)  S(1)
    * */
    public  static  int Fibonacci(int n){
        if(n<=1){
            return 1;
        }else{
            int first=1,second=1,sum=1;
            //i等于0 1时,不进入循环,直接输出sum
            for(int i=2;i<=n;i++){
                sum=first+second;
                first=second;
                second=sum;
            }
            return sum;
        }
    }

二分查询

步骤

首先,假设表中元素是按升序排列,
将表中间位置记录的关键字与查找关键字比较,
  如果两者相等,则查找成功;
  否则利用中间位置记录将表分成前、后两个子表,
   如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,
   否则进一步查找后一子表。
重复以上过程,
  直到找到满足条件的记录,使查找成功,
  或直到子表不存在为止,此时查找不成功。

思考

  • 有重复值时,怎么找到最右边元素的下标
    在这里插入图片描述
  • 如果数组中间没有有序
  • 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。
    如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

递归实现

  /*
     如果数组是从小到大有序的,使用递归和非递归实现二分查询。
   * */
   public static int binarySearch(int[] array,int tar,int  high,int low){
       int pos=-1;
       if(low<=high){//记住有个等于
           int mid=( (high-low)>>1 ) +low;//加法优先级高
           if(array[mid]==tar){
               while(mid<high && array[mid+1]==tar)  ++mid;//找最右边的(注意:<high,才能保证mid+1不越界)
               pos= mid;

           }else if(tar<array[mid]){
               pos= binarySearch(array,tar,low,mid-1);
           }else if(tar>array[mid]){
               pos= binarySearch(array,tar,mid+1,high);
           }
       }
       return pos;
   }

非递归实现

//   常见命名 arr  pos  index  target  left  right
    public static int binSearch(int[] array,int num){
        int low=0;
        int high=array.length-1;//length不带括号,因为是属性
        int pos=-1;
        //记住有个等于号,指向同一个数据也要查
        while (low<=high) {
            int mid=(high-low)/2+low;//比(low+high)/2好在  防止超范围
            if (array[mid] > num){
                high=mid-1;
            }else if (array[mid] < num) {
                low=mid+1;
           }else{
               // 如果有重复值,怎么找到最左边元素的下标???????
               while(mid>low && array[mid-1]==num)
                   --mid;//0号元素值也是的时候,减减会越界;最小为low+1时,--一下也不会越界
//                // 如果有重复值,怎么找到最右边元素的下标???????
//                while(mid<high && array[mid+1]==num)
//                    ++mid;//0号元素值也是的时候,减减会越界;最小为low+1时,--一下也不会越界
                pos=mid;

               //  如果数组中间没有有序???????????????
                break;
            }
         }
        return pos;
    }

全排列

分析

代码

Perm
    private static void Perm(int[] arr, int k, int m) {
        //k m是要排序字母的起点和终点   位置重合,即只剩一个元素
        if(k==m){
            for(int i=0;i<=m;i++){
                System.out.print(arr[i]+" ");
            }
            System.out.println();
            //执行到此,此层perm已结束,回退
        }else{
            for(int j=k;j<=m;j++){
                Swap_Arr(arr,j,k);//固定一个
                Perm(arr,k+1,m);//规模减小
                Swap_Arr(arr,j,k);//处理完一个首字母的全排列后,回退,保证原数组仍为1 2 3  不然输出结果发现 1 2 3重复
            }
        }
    }
    //j位置是要固定的元素,应该放到最前面    K是要排列元素的起点位置下标
    private static void Swap_Arr(int[] arr, int j, int k) {
            if(k!=j){
                int tmp=arr[j];
                arr[j]=arr[k];
                arr[k]=tmp;
            }
    }

Main
      int[] arr={1,2,3};
       Perm(arr,0,arr.length-1);

子集问题

分析

在这里插入图片描述

代码

get_Subset
//找所有子集
    private  static void get_Subset(int[]  arr,int[] brr,int i,int n){
        if(i>=n){
            boolean zeroFlag=true;
            for(int j=0;j<n;j++){//即0.。。length-1  有选择的输出
                if(brr[j]==1){
                    System.out.print(arr[j]+" ");
                    zeroFlag=false;
                }
            }
            if(zeroFlag==true)
                System.out.print("#");
            System.out.println();
        }else{
            //两个get_Subset,像二叉树
            brr[i]=1;//1表示走左边
            get_Subset(arr,brr,i+1,n);//左子树
            brr[i]=0;
            get_Subset(arr,brr,i+1,n);//右子树
        }
    }
Main
 //子集
        int[] arr={1,2,3};//数组
        int[] brr={0,0,0};//打印标识
        get_Subset(arr,brr,0,arr.length);//输出标识 对应 length层的树的叶节点的路径,注意wps图

输出结果
1 2 3 
1 2 
1 3 
1 
2 3 
2 
3 
#

青蛙跳台阶问题

题目1:

一只青蛙一次可以跳上1级台阶,也可以跳上2级。
求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

思路

本质是斐波那契数列
假设n个台阶,一共有f(n)个方案,则 f(n)=f(n-1)+f(n-2)
即一次跳一个时,跳剩下台阶的所有可能性 与 一次跳两个时 剩下的所有可能性

递归
public static int Fog(int n) {
        //特例
        if (n == 0)
            return 0;
        if (n == 1)
            return 1;
        if (n == 2)
            return 2;

        return Fog(n - 1) + Fog(n - 2);//双分支递归,调用形式像二叉树,层层往下计算,会有重复计算
    }

非递归
public static int NiceFog(int n) {
        //特例
        if (n == 0)
            return 0;
        if (n == 1)
            return 1;
        if (n == 2)
            return 2;

        int n1=1;
        int n2=2;
        int count=2;
    /*
        F(n-2)   F(n-1)  F(n)  F(n+1)
        n1        n2      ?
                  n1      n2      ?
    */
        while (count++ <= n){
            int tmp=n1;
            n1=n2;
            n2=tmp+n2;
        }
        return n2;
  }

题目2:

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上 n 级。
求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

思路

在这里插入图片描述

FogN_N
 public int FogN_N(int n) {
        if(n==0){
            return 0;
        }else if (n == 1) {
            return 1;
        } else {
            return 2 * FogN_N(n - 1);
        }
    }

题目3

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上m级。
求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

思路
n<=m时,   1  2  3 ... n ...m   等价于上面的FogN_N,可以一次跳1、2、、、、n级到第n级台阶

n>m时:   1  2  3  ...  m  ...n
列多项式,适当的展开,然后替换:
   f(n) =   f(n-1) + f(n-2) + f(n-3) + ... + f(n-m)
 f(n-1) =   f(n-2) + f(n-3) + ... + f(n-m) + f(n-m-1)
化简得:f(n) = 2f(n-1) - f(n-m-1)
FogM_N
public int FogM_N(int n,int m) {
        //当大于m的时候是上面的公式
        if(n > m){
            return 2*FogM_N(n-1, m)-FogM_N(n-1-m, m);
        }
        //当小于等于m的时候就是和n级的相同了
        if(n==0){
            return 0;
        }else if (n == 1) {
            return 1;
        } else {
            return 2 * FogM_N(n - 1,n);
        }
    }

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值