Java算法学习4——分治,记忆化递归与快速幂

一、分治

分治其实就是将一个大问题分解为一个个小问题,然后再将每个子问题求出结果,最后将子问题结果合并为原问题的解。分治问题常常用递归方式来解决

二、递归

递归一定要记住递归体与临界点,下面为递归的两个例子

1.输出1…n的全排列

思路:可以分解成输出1为开头的全排列,2开头的全排列,3开头的全排列……的子问题来解决,然后利用一个hashtable数组,记录这个数字是否以及存在数组P中了,如果没有的话就加入,然后hashtable变为true,最后递归结束就把hashtable变成false,下面例子为生成n=3的全排列.从p[0]开始填。

import java.util.*;

public class Main(){

 private int p=new int [4];
 private boolean hashtable= new boolean [4];

public void init(){
for(int i=0;i<4;i++){
p[i]=0;
hashtable[i]=false;
}
}

public void genertateP(int index){
if(index==4){
for(int i=0;i<4;i++){
System.out.print(p[i]+" ");
return ;
}
for(int i=1;i<4;i++){
if(hashtable[i]==false){
p[index]=i;
hashtable[i]=true;
genertateP(index+1);
hashtable[index]=false;
}
}

public static void main (String []args){
genertatep(0);从p[0]开始填
}

2.n皇后问题

利用递归回溯法:就是对于每一列,进行行数的全排列,然后如果不在对角线上的就成立输出。

import java.util.*;
import java.Math.*;

public class Main(){
private int []P=new int [100];//用来存每一列的行位置,下标为列,元素为行数
private boolean []hashtable=new boolean [100];//用来判断数字有无用过
private int count=0;
private int n=0;//一共有n行

public Main(int index){
this.n=n;
}
public class generateP(int index){

if(index==n+1){
count++;
return;
}

for(int i=1;i<n;i++){
if(hashtable[i]==false){
boolean flag=false;
for(int j=1;j<index;i++){//检测之前的每一列的行数是否符合皇后布局
if(Math.abs(j-index)==Math.abs(p[j]-i){
flag=true;
}
}
if (flag==false){
hashtable[i]==true;
p[index]=i;
generateP(index+1);
hashtable[i]=false;
}
}
}

public static void main(String []args){
Main a=new Main(100);
for(int i=0;i<100;i++){
hashtable[i]=false;
}
generateP(1);
System.out.print(count);
}


三、记忆化递归

1.解释

记忆化递归其实就是将每次递归的结果记忆下来,这样当在某一次递归时,如果要用到以前的递归结果,那么就可以直接调用。而动态规划就是用来解决那些具有相同步骤事。当需要用到动态规划的时候,可以直接用记忆化递归来代替动态规划。

https://blog.csdn.net/su_bao/article/details/81181975

2.记忆化递归的求解模板
在这里插入图片描述

红色区域是边界区域。蓝色区域是为了求解子问题。绿色区域是当问题求结果,则直接返回。即:

①第一步:简历一个数组用来存递归的结果同时确定递归出口临界值
②第二步:当if(!m[i]),即当没有递归过这次时,就开始执行原来递归形式的递归体,然后将执行结果,存到数组中
③第三步:如果以前执行过,就直接返回。return数组的值

3.例题

(1)Fibonacci数列
https://zhuanlan.zhihu.com/p/73579773

(2)洛谷
在这里插入图片描述
解决:
C++

#include<iostream>

using namespace std;
int a[1005];
int count(int n)
{
    if(n==1)return 0;
    long long ans=0;
    if(a[n]==0)//如果没有计算过这个数,计算并储存在a[n]中
    {
        for(int i=1;i<n;i++)
        if(i*2<n||i*2==n)//其实就相当于for(int i=1;2*i<=n;i++)
            ans+=count(i)+1;//加一的原因是因为,题目中数量的计算是包括它本身的
    return a[n]=ans;
    }
    else//计算过这个数的话直接返回这个数对应值
        return a[n];
}

int main ()
{
    int n;
    cin>>n;
    cout<<count(n)+1;
    return 0;
}

Java


import java.util.*;

public class Main(){
	private int m=new int [1001];
	private int n=0;
	private count=0;
	
	Main(int index)
	{
		this.n=index;
	}
	
	public int generate(int index){
		if(index==0){
			return 0;
		}
		if(index==1){
			return 0;
		}
		if(!m[index]){
			int t=0;
			for(int i=1;i<=index/2;i++){
				t=t+generate(i)+1;//易错点:要加上1,因为还有他自己本身也算一个 
			}
			m[index]=t;
		}
		return m[index];
	}
	
	public int print(int n){
		return m[n];
	}
	
	public static void main(){
		Main a=new Main(6);
		a.generate(6);
		System.out.print(a.print(6));
	}
} 

四、快速幂

快速幂就是用来快速计算一个数的m次幂的方法,问题引入如下:

给定一个正整数a、b、m,其中a<10^ 9 ,b<10^ 9 , 1<m<10^ 9 ,求a^ b%m

以后这种类型的解决思路:
采用递归的方法
①当b是奇数时,有a^ b=a*a^(b-1)
②当b为偶数时,有a^ b=a^ ( b/2) *a^(b/2)

import java.util.*;

public class Main(){
Main();
public long binaryPow(long a,long b,long m){
if(b==0) return 1;
if(b%2==1) return a*binaryPow(a,b-1,m);
if(b%2==0) {
long mul=binaryPow(a,b/2,m);
return mul*mul;
}
} 
 

注意:
①如果初始值 a有可能大于m时,那么进入函数前就可以先让a对m取模,根据数学性质可以知道,是不变的(aa)%m=a%ma%m

②如果一开始m=1的话,那么无论多少取模都为0,所以在进入函数的时候,可以设一个分支点。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值