算法学习之深入递归

1、“逐步生成结果”类问题–数值型

自下而上的递归(递推、数学归纳、动态规划) 在这里插入图片描述

1、走楼梯

1、问题
在这里插入图片描述
2、思路
在这里插入图片描述
(1)第一步选择(4台阶情况):
第一步走1梯:剩下的就是3梯的情况
第一步走2梯:剩下的就是2梯的情况
第一步走3梯:剩下的就是1梯的情况
(2)递推公式 n>3 f(n)=f(n-3)+f(n-2)+f(n-1)

2、代码

package com.lanqiao.eight;

public class tizi {
	public static void main(String[] args) {
		System.out.println(f(5));
	}
	public static int f(int n)
	{
		if(n==0) return 1;//注意!,比如f(3)第一步走三梯,直接是f(0)这也是一种情况,所以应该是1
		if(n==1) return 1;
		if(n==2) return 2;
		if(n==3) return 4;
		return f(n-3)+f(n-2)+f(n-1);
	}
}

3、注意点
(1)注意f(0)=1

2、机器人走方格

1、题目
在这里插入图片描述
2、思想
(1)递推公式解法
在这里插入图片描述
(1)只有一行或一列:只有1种走法
(2)2行2列:2种走法(也是向下:单行单列,向右:单行单列)
(3)2行3列:向下走一个就是2行2列的情况(2),向右走一个就是单行单列的情况(1)
3行2列:向右走就是2行2列的情况(2),向下走就是单行单列的情况
(4)3行3列:右走:2行3列,下走:3行2列
(5)递推公式:f(x,y)=f(x,y-1)+f(x-1,y)

(2)直接迭代解法
在这里插入图片描述
每个格子都是相邻两个格子相加
二维数组行数代表有多少行,列数数字代表有多少列

3、代码

package com.lanqiao.eight;

public class wangge {
	public static void main(String[] args) {
		System.out.println(f(3,3));
		System.out.println(f2(3,3));
	}
	public static int f(int x,int y)
	{
		if(x==1||y==1) return 1;
		return f(x,y-1)+f(x-1,y);
	}
	public static int f2(int x,int y)
	{
		int[][] ans = new int[x+1][y+1];
		for(int i=1;i<=x;i++)//第一列填充满
		{
			ans[i][1]=1;
		}
		for (int i = 1; i <=y; i++) {
			ans[1][i]=1;
		}
		for(int i=2;i<=x;i++)
		{
			for(int j=2;j<=y;j++)
			{
				ans[i][j]=ans[i][j-1]+ans[i-1][j];
			}
		}
		return ans[x][y];
	}
}

1、“逐步生成结果”类问题–非数值型在这里插入图片描述

1、逐步生成括号

1、问题
给出一个数字,相当于给出几个括号,判断排列方式

2、思想
(1)Set集合特点:不会存储相同元素(去重)
(2)Set两个实现类:HashSet TreeSet(按红黑树有序排列其中元素)
(3)Set使用方法参照:https://www.cnblogs.com/xiaxj/p/7891963.html
(4)借助Set的去重特点,向其中不断加入元素,遍历到所有情况

3、代码

package com.lanqiao.eight;

import java.util.HashSet;
import java.util.Set;

public class kuohao {
	public static void main(String[] args) {
		Set<String> ans = f(3);
		for(String e:ans)
		{
			System.out.println(e);
		}
	}
	public static Set<String> f(int n)
	{
		Set<String> ans = new HashSet<String>();
		ans.add("()");
		if(n==1) return ans;
		for(int i=2;i<=n;i++)
		{
			Set<String> ans1 = new HashSet<String>();
			for(String e:ans)
			{
				ans1.add(e+"()");
				ans1.add("()"+e);
				ans1.add("("+e+")");
			}
			ans = ans1;
		}
		return ans;
	}
}

2、列出所有子集

1、问题
在这里插入图片描述
2、思路
在这里插入图片描述
(1)进行递归思考时,一般是从1~n进行递推思考
(2)从下往上逐步减小cur,对每个元素是否加入到当前序列分情况考虑。
(3)边界条件cur=0即考虑第一个元素,此时要考虑将空集加入到集合中,因为只有加了空集才会出现{2},{3},{4}等这些情况

方法2:
(1)二进制法则,根据元素个数计算出应该有的自己个数2^n-1,根据子集个数进行循环
(2)将自己转为2进制,通过左移查看每一位是1还是0,是1就将这一位的元素添加到当前集合中
3、代码

package com.lanqiao.eight;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

public class ziji {
	public static void main(String[] args) {
		int[] arr = {1,2,3};
//		Set<Set<Integer>> ans = getSet(arr, arr.length-1, arr.length-1);
//		for(Set set:ans)
//		{
//			System.out.print("{");
//			for(Object a:set)
//			{
//				System.out.print(a+",");
//			}
//			System.out.print("}");
//			System.out.println();
//		}
		ArrayList<ArrayList<Integer>> ans2 = getSet2(arr, arr.length);
		for(ArrayList<Integer> a:ans2)
		{
			System.out.print("{");
			for(Integer i:a)
			{
				System.out.print(i+" ");
			}
			System.out.print("}");
			System.out.println();
		}
	}
	/**
	 * 
	 * @param arr 要处理的元素
	 * @param n 元素个数
	 * @param cur 当前处理元素下标
	 * @return
	 */
	public static Set<Set<Integer>> getSet(int[] arr,int n,int cur)
	{
		Set<Set<Integer>> ans = new HashSet<>();
		if(cur==0)//处理到第一个元素,添加空集
		{
			Set<Integer> nil = new HashSet<>();//建立空集,HashSet不用任何例子
			Set<Integer> first = new HashSet<>();
			first.add(arr[0]);
			ans.add(nil);
			ans.add(first);
			return ans;
		}
		Set<Set<Integer>> last = getSet(arr,n,cur-1);
		for(Set<Integer> set:last)//对得到的上个集合中每个集合判定当前元素加还是不加
		{
			ans.add(set);//保留上个集合原样
			Set<Integer> clone = (Set<Integer>)((HashSet)set).clone();
			clone.add(arr[cur]);//关键点:如果是原set添加则会导致上面添加的set也会改变:地址引用
			ans.add(clone);
		}
		return ans;
	}
	public static ArrayList<ArrayList<Integer>> getSet2(int[] arr,int n)
	{
		ArrayList<ArrayList<Integer>> ans = new ArrayList<>();
		for(int i=(int)Math.pow(2, arr.length);i>0;i--) 
		{
			ArrayList<Integer> s = new ArrayList<>();
			for(int j =n-1;j>=0;j--)
			{
				if(((i>>j)&1)==1)//
				{
					s.add(arr[j]);
				}
			}
			ans.add(s);
		}
		return ans;
	}
}

4、注意点
(1)建立空集,HashSet不用任何例子
(2)如果是原set添加则会导致上面添加的set也会改变:地址引用,所以调用clone方法克隆一个Set

3、全排列

1、问题
给定一个字符串,列出其全排列的情况
2、思想
思想1:
(1)先将第一个字符加入到结果数组中,对后面每个字符分别放在数组中已放好元素的前、后、中间三种情况,直到最后一个数组完成
思想2:
(1)利用回溯思想
(2)先将字符串转为字符数组,对字符数组中每一对元素进行一次翻转,翻转完成对下一对进行翻转,翻转到最后一个字符,加入到结果数组,并返回上一层,将翻转字符数组恢复。
(3)注意要恢复已经翻转的数组
(4)回溯法参照:https://blog.csdn.net/qq_43313035/article/details/93124719
排列树:
在这里插入图片描述
3、代码

package com.lanqiao.eight;

import java.util.ArrayList;
import java.util.Arrays;

import com.lanqiao.vidio.Utils;

public class quanpailie {
	public static void main(String[] args) {
		ArrayList<String> ans = getArr2("abc");
		for(String s:ans)
		{
			System.out.print(s+" ");
		}
	}
	public static ArrayList<String> getArr(String s)
	{
		ArrayList<String> ans = new ArrayList<>();
		int n = s.length();
		ans.add(s.charAt(0)+"");//注意!char转String方法
		for(int i=1;i<n;i++)
		{
			ArrayList<String> newArr = new ArrayList<>();
			for(int j = 0;j<ans.size();j++)
			{
				String newStr = s.charAt(i)+ans.get(j);//加在前面
				newArr.add(newStr);
				newStr = ans.get(j)+s.charAt(i);//加在后面
				newArr.add(newStr);
				for(int k=1;k<ans.get(j).length();k++)//加在中间
				{
					newStr = ans.get(j).substring(0,k)+s.charAt(i)+ans.get(j).substring(k);//subString是从中间截取到最后
					newArr.add(newStr);
				}
			}
			ans=newArr;
		}
		return ans;
	}
	
	//2、回溯法
	static ArrayList<String> ans = new ArrayList<>();
	public static ArrayList<String> getArr2(String s)
	{
		
		char[] arrList = s.toCharArray();
		Arrays.sort(arrList);
		paiLie(arrList,0);
		return ans;
	}
	
	private static void paiLie(char[] arr, int k) {
		if(k==arr.length)
		{
			ans.add(new String(arr));
		}
		for(int i=k;i<arr.length;i++)
		{
			Utils.swap(arr, k, i);
			paiLie(arr,k+1);
			Utils.swap(arr, k, i);
		}
	}
}

3、封闭形式直接解

1、汉诺塔移动次数

在这里插入图片描述(1)n个圆盘,先将n-1个从a->c f(n)=f(n-1)
(2) 将最后一个盘子挪到b f(n)=f(n-1)+1
(3) 将c柱的n-1个盘子挪到b f(n)=2f(n-1)+1
最终总结出f(n)=2^n -1
数学归纳法:

得出封闭式解,可以直接算

2、斐波那契数列第n项

在这里插入图片描述可以参考:https://blog.csdn.net/qq_38930523/article/details/80303694
总结:斐波那契第n项就是
[1,1] *第二个矩阵的n-2次方次

3、上楼梯

在这里插入图片描述
思想:
两个矩阵乘一次是 f2,f3,f1+f2+f3=f4 得到f4
乘右边两次是 f3,f4,f5 得到f5
…不断乘右边矩阵最后得到结果

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值