当前位置: 首页 > news >正文

递归与分治算法(1)--经典递归、分治问题

目录

一、递归问题

1、斐波那契数列

2、汉诺塔问题

3、全排列问题

4、整数划分问题

二、递归式求解

1、代入法

2、递归树法

3、主定理法

三、 分治问题

1、二分搜索

2、大整数乘法


一、递归问题

1、斐波那契数列

        斐波那契数列不用过多介绍,斐波那契提出的繁殖兔子问题。

        斐波那契递推式如下:

F(n)=\begin{Bmatrix} 1\qquad \qquad \qquad \qquad \quad \ n=0\\ 1 \qquad \qquad \qquad \qquad \quad \ n=1\\ F(n-1)+F(n-2) \quad n>1 \end{Bmatrix} 

        斐波那契代码:

//斐波那契数列
import java.util.Scanner;
public class Fibonacci {public static void main(String [] args){int input=new Scanner(System.in).nextInt();System.out.println(factorial(input));}public static int factorial(int n){if(n==0||n==1)return 1;else{return factorial(n-1)+factorial(n-2);}}
}

2、汉诺塔问题

        汉诺塔问题,也是一个经典问题。一般分为三个步骤:

(1)把n-1个盘子从A柱移到B柱

(2)把最底层1个盘子从A柱移到C柱

(3)把B柱n-1个盘子移到C柱。

        完整代码: 

package RecursionAndDivide;
import java.util.Scanner;
public class Hanoi {public static void main(String[] args){int input=new Scanner(System.in).nextInt();move(input,'A','B','C');} public static void move(int n,char a,char b,char c){if(n==1)System.out.println(a+"->"+c);   //else {move(n-1,a,c,b);    //n-1个从a移到bmove(1,a,b,c);      //底层1个从a移到cmove(n-1,b,a,c);    //n-1个从b移到c}}
}

3、全排列问题

        使用递归的方式输出数组中的全排列,步骤如下:

(0)判定是否数组中仅有一个数,那么输出该排列形式。(请注意,k作为固定头,在每一层进行修改排列顺序的头部)

(1)首先将第一个数与后面的每一个数进行交换(这是一个k到m的循环)。得到:

        1,2,3,4,...,n

        2,1,3,4,...,n

        3,1,2,4,...,n

(2)计算除第一个数以外后面的全排列。

(3)交换第一个数与刚刚那个数,使数组恢复。

        k作为固定头,m作为尾,一直不改变,i作为固定头与循环中交换的数的索引。固定头依赖于第几层递归,不同的递归只会改变数组排列的方式,不会改变长度,而尾不动,所以每次输出只需要输出0到m索引的数组数,也就是所有数组的数。

完整代码:

//全排列问题
public class Permutations {public static void main(String [] args){int []list={1,2,3,4,5};int k=0;int m=list.length-1;Perm(list,k,m);} //产生全排列public static void Perm(int []list,int k,int m){if(k==m)      {for(int i=0;i<=m;i++)System.out.print(list[i]);System.out.println();}else{for(int i=k;i<=m;i++){swap(list,i,k);        Perm(list,k+1,m);swap(list,i,k);}}}//交换数组中两个元素public static void swap(int []list,int i,int k){int temp=list[i];list[i]=list[k];list[k]=temp;}
}

4、整数划分问题

        整数划分问题就是把一个正整数拆分成若干个正整数相加的所有方式,也可以使用递归来求解。定义p(n)为正整数n的划分总个数,q(n,m)为正整数n划分为最大值为m的划分总个数。

        有以下递归式成立:

q(n,m)=\begin{Bmatrix} 1 \qquad \quad \qquad \qquad \qquad \qquad n=1,m=1 \\ q(n,n) \qquad \qquad \qquad \qquad \qquad \quad n<m\\ 1+q(n,n-1) \quad \qquad \qquad \ \ \qquad n=m\\ q(n,m-1)+q(n-m,m) \quad n>m>1 \end{Bmatrix}

        完整代码: 

public class IntegerDivide {public static void main(String[] args){System.out.println(q(6,6));}public static int q(int n, int m){if((n==1)||(m==1))   //且和或一样,或的话出现q(1,2)会执行条件2,返回q(1,1)return 1;if(n<m)return q(n,n);if(n==m)return 1+q(n,n-1);return q(n,m-1)+q(n-m,m);} 
}

二、递归式求解

1、代入法

        一般来说如果递归式成倍数下降,可以n取指数形式,平衡递归式的麻烦。换元求解就是不断回带递归式,最后得到T(1)项忽略,其他换元回变量n的项。

2、递归树法

        递归树法就是将常数项逐层展开成树叶子结点的形式,并将每一层的量相加的总和为递归式的解。如下面这个题,根节点就是递归式中的常数项n-1,每一层叶子点个数就是子递推式的系数为2,叶子结点的量只用n/2换元上一节点的量,得到n/2-1,以此类推,那么递推树如下。

         计算递推式每一层的和,注意最后一层以1为结束,当使用n=2^k替代时,请注意层数变化,最后一层是\frac{n}{2^{k-1}}-1=\frac{2^k}{2^{k-1}}-1=1。那么总和如下,记得要用n替换k:

        对于这一类递归树有偏重的题,可以参照下面这个做法,找到最慢下去的叶节点路线,就是大O的复杂度。

3、主定理法

        主定理方法有一定局限性,最没有局限性的是代入法,但是很麻烦。

三、 分治问题

1、二分搜索

        二分搜索原理:

(1)初始数组是已经排好序的,目的是某个数值是否存在,并找到他的索引,否则返回-1。

(2)大循环满足左值小于等于右值,每次取中间值,判断中间值是否为给定数值,若是返回索引。

(3)判断是否小于中间值,若是右值等于mid-1,判断是否大于中间值,若是左值等于mid+1。

(4)如果不满足循环条件,还没有找到给定值,那么返回-1。

        二分搜索算法的复杂度为O(logn),应该建立在已排好序的数组上,如果为了搜索而去排序,复杂度就大大提升了。

//二分搜索
public static int Search(int arr[],int x,int n){int left=0;int right=n-1;while(left<=right){int mid=(left+right)/2;if(x==arr[mid])return mid;if(x>arr[mid])left=mid+1;else right=mid-1;}return -1;} 

2、大整数乘法

        由于大整数本身就会出现精度丢失问题,另外乘积数过大也会出现数值溢出的问题。所以可以用数组存储大整数进行两个数组之间的乘法。

        另外高复杂度O(mn)也是不能忽视的问题,可以通过大整数拆成两段,将中间二次计算的环节存入内存,来减少复杂度。

        比如下图X和Y都为n为二进制整数,计算它们的乘积,可以拆分成两个相等的n/2位的结构。

        XY=(A*2^{n/2}+B)(C*2^{n/2}+D)=AC*2^n+(AD+BC)*2^{n/2}+BD\\ XY=AC*2^n+((A-B)(D-C)+AC+BD)*2^{n/2}+BD 

        根据上面的式子改变,BD这一项就重复计算了,乘法计算的次数整体不变,但是BD可以不用二次计算了,这样就可以减少一次乘法计算,6次乘法变为5次乘法。 复杂度相比于O(n^2)也有所降低。

        相类似的,矩阵乘法也可以用这种方式降低时间复杂度,成为Strassen矩阵乘法。

 

http://www.lryc.cn/news/181525.html

相关文章:

  • Java之SpringCloud Alibaba【六】【Alibaba微服务分布式事务组件—Seata】
  • Android逆向学习(五)app进行动态调试
  • 音频编辑软件Steinberg SpectraLayers Pro mac中文软件介绍
  • 基于.Net Core实现自定义皮肤WidForm窗口
  • 【Rust】操作日期与时间
  • blender快捷键
  • java Spring Boot 自动启动热部署 (别再改点东西就要重启啦)
  • TouchGFX之后端通信
  • cesium gltf控制
  • Spring的依赖注入(DI)以及优缺点
  • 【强化学习】05 —— 基于无模型的强化学习(Prediction)
  • 【计算机组成原理】考研真题攻克与重点知识点剖析 - 第 1 篇:计算机系统概述
  • 【Java-LangChain:面向开发者的提示工程-8】聊天机器人
  • 利用t.ppft.interval分别计算T分布置信区间[实例]
  • 软件工程第三周
  • 动态链接那些事
  • 力扣:118. 杨辉三角(Python3)
  • QGIS文章二——DEM高程裁剪和3D地形图
  • 【kubernetes】kubernetes中的StatefulSet使用
  • 创建文件夹
  • 点击router-link时候会发生什么?
  • 【Spring】@Bean方法中存在继承如何分析
  • 【Vim 插件管理器】Vim-plug和Vim-vbundle的区别
  • 电子计算机核心发展(继电器-真空管-晶体管)
  • SDI-12协议与STM32 进行uart通信
  • JS中的强制类型转换
  • WebSocket实战之四WSS配置
  • veImageX 演进之路:Web 图片加载提速50%
  • WebSocket实战之五JSR356
  • flask-sqlalchemy结合Blueprint遇到循环引入问题的解决方案