算法-暴力递归

暴力递归
在这里插入图片描述
在这里插入图片描述

public class HanNuoTa {
    
    
	public static void hannuo(int n) {
    
    
		if(n>0) {
    
    
			function(n,"左","右","中");
		}
		
	}

	public static void function(int i, String start, String end, String other) {
    
    
		if(i==1) {
    
    
			System.out.println("Move 1 from"+start+"to"+end);
		}else {
    
    
			function(i-1,start,other,end);//第一步,把i-1个从初始移动到其他
			System.out.println("Move "+i+"from "+start+"to "+end);//第二步,把第i个从初始移动到目的地
			function(i-1,other,end,start);//第三步:把之前的i-1个从其他移动到目的地 
		}
		
	}
	public static void main(String[] args) {
    
    
		int n=4;
		hannuo(n);
	}

}

在这里插入图片描述在这里插入图片描述abc,ab,ac,a,bc,b,c,“”(我没写代码)
在这里插入图片描述
打印一个字符串的全排列,可以出现重复的排列,下面是它的代码

/*字符串的全排列*/
public class StringsQuanPaiLie {
    
    
	public static ArrayList<String> QuanPaiLie(String str){
    
    
		ArrayList<String> res=new ArrayList<>();
		if(str==null||str.length()==0) {
    
    
			return res ;
		}
		char[] chs=str.toCharArray();
		process(chs,0,res);
		res.sort(null);
		return res;
		
	}

	public static void process(char[] str, int i, ArrayList<String> res) {
    
    
		if(i==str.length) {
    
    //如果来到了末尾
			res.add(String.valueOf(str));//str承载了所有之前的选择
		}
		for(int j=i;j<str.length;j++) {
    
    //i往后所有的字符都可以来到i位置,
			swap(str,i,j);
			process(str,i+1,res);
			swap(str,i,j);
		}
		
	}

	public static void swap(char[] str, int i, int j) {
    
    
		char tmp=str[i];
		str[i]=str[j];
		str[j]=tmp;
		
	}
	
	public static void main(String[] args) {
    
    
		ArrayList<String> result=QuanPaiLie("abbb");
		
		for(int i=0;i<result.size();i++) {
    
    
			System.out.println(i+"---"+result.get(i));
		}
	}
}

运行结果:
在这里插入图片描述

打印一个字符串的全排列,不出现重复的排列,下面是它的代码

/*字符串的全排列,不重复*/
public class StringsQuanPaiLieUnique {
    
    
	public static ArrayList<String> QuanPaiLieUnique(String str){
    
    
		ArrayList<String> res=new ArrayList<>();
		if(str==null||str.length()==0) {
    
    
			return res ;
		}
		char[] chs=str.toCharArray();
		process(chs,0,res);
		res.sort(null);
		return res;
		
	}

	public static void process(char[] str, int i, ArrayList<String> res) {
    
    
		if(i==str.length) {
    
    //如果来到了末尾
			res.add(String.valueOf(str));//str承载了所有之前的选择
		}
		boolean[] visit=new boolean[26];//visit[0,1,2...25]表示a,b,c,d...z试过还是没试过
		for(int j=i;j<str.length;j++) {
    
    //i往后所有的字符都可以来到i位置,
			if(!visit[str[j]-'a']) {
    
    //如果当前字符没试过,就试,注册上
				visit[str[j]-'a']=true;
				swap(str,i,j);
				process(str,i+1,res);
				swap(str,i,j);
			}
			
		}
		
	}

	public static void swap(char[] str, int i, int j) {
    
    
		char tmp=str[i];
		str[i]=str[j];
		str[j]=tmp;
		
	}
	
	public static void main(String[] args) {
    
    
		ArrayList<String> result=QuanPaiLieUnique("abbb");
		
		for(int i=0;i<result.size();i++) {
    
    
			System.out.println(i+"---"+result.get(i));
		}
	}
}

运行结果:
在这里插入图片描述

在这里插入图片描述先手函数定义
在这里插入图片描述后手函数定义
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述代码:


/*系统栈结构逆序,能帮助理解递归
 * https://www.bilibili.com/video/BV13g41157hK?p=10
 * 1:39:40*/
public class SystemStackReverse {
    
    
	public static void reverse(Stack<Integer> stack) {
    
    
		if(stack.isEmpty()) {
    
    
			return;
		}
		int i=f(stack);
		reverse(stack);
		stack.push(i);
	}
	public static int f(Stack<Integer> stack) {
    
    
		int result=stack.pop();
		if(stack.isEmpty()) {
    
    
			return result;
		}else {
    
    
			int last=f(stack);
			stack.push(result);
			return last;
		}	
	}
	public static void main(String[] args) {
    
    
		Stack<Integer> test=new Stack<Integer>();
		test.push(1);
		test.push(2);
		test.push(3);
		test.push(4);
		test.push(5);
		test.push(6);
		reverse(test);
		while(!test.isEmpty()) {
    
    
			System.out.println(test.pop());
		}
	}
}

在这里插入图片描述代码片

/*规定1和A对应、2和B对应、3和C对应...
那么一个数字字符串比如"111",就可以转化为"AAA”、“KA"和"AK"
给定一个只有数字字符组成的字符串str,返回有多少种转化结果。
*/
public class NumToChar {
    
    
	public static int number(String s) {
    
    
		char[] intChar=s.toCharArray();
		return process(intChar,0);
	}
	public static int process(char[] str,int i) {
    
    
		if(i==str.length) {
    
    //如果i来到了字符串结束的位置,说明可以作为一种结果返回
			return 1;
		}
		if(str[i]=='0') {
    
    
			return 0;//如果当前来到了字符0,说明后面走不通了
		}
		if(str[i]=='1') {
    
    
			int res=process(str,i+1);//i自己作为单独的部分,后续有多少种方法
			if(i+1<str.length) {
    
    
				res+=process(str,i+2);//(i和i+1)作为单独的部分,后续有多少种方法
			}
			return res;
		}
		if(str[i]=='2') {
    
    
			int res=process(str,i+1);//i自己作为单独的部分,后续有多少种方法
			if(i+1<str.length&&(str[i+1]>='0'&&str[i+1]<='6')) {
    
    
				//i和i+1作为单独的部分并且没有超过26,后续有多少种方法
				res+=process(str,i+2);
			}
			return res;
		}
		//str[i]=='3'-'9'时
		return process(str,i+1);
	}
	public static void main(String[] args) {
    
    
		System.out.println(number("11"));
	}

}

在这里插入图片描述思路:就是试,0号货要或不要,1号货要或不要…,【测试代码有错误,就不展示了】

暴力递归上

在这里插入图片描述在这里插入图片描述在这里插入图片描述递归解法

/**
 * 马要跳到(a,b)位置,总共走K步
 * 总共9列10行*/
public class HorseJump {
    
    
	public static int getWays(int x,int y,int k) {
    
    
		return process(x,y,k);
		
	}
	//从(0,0)出发,跳step步来到(a,b)位置的方法数有多少种,a是行,b是列,(a,b)是目标位置
	public static int process(int a, int b, int step) {
    
    
		if(a<0||a>8||b<0||b>9) {
    
    
			return 0;//无法到达
		}
		if(step==0){
    
    //已经跳完,不能动了
			return (a==0&&b==0)?1:0;
		}
		//不越界也有步数可以跳
		return process(a-1,b+2,step-1)//从以下位置跳一步就可以到达(a,b),从(0,0)出发跳step-1步到达这些位置就可以了
				+process(a-2,b+1,step-1)
				+process(a+1,b+2,step-1)
				+process(a+2,b+1,step-1)
				+process(a+1,b-2,step-1)
				+process(a+2,b-1,step-1)
				+process(a-1,b-2,step-1)
				+process(a-2,b-1,step-1);
		
	}
	public static void main(String[] args) {
    
    
		//测试从(0,0)出发走step=10,到达(6,6)的方法数有多少种
		int res=getWays(6,6,10);
		System.out.println(res);
	}

}

改为动态规划
如果是一个体的话,要的是最高层的值,也是一个面,(具体是那=哪一个值,由x,y决定)
在这里插入图片描述

public static int dpWays(int x,int y,int step) {
    
    //走step步到达(x,y),有多少种走法
		if(x<0||x>8||y<0||y>9||step<0) {
    
    
			return 0;
		}
		int[][][] dp=new int[9][10][step+1];
			dp[0][0][0]=1;
			for(int h=1;h<=step;h++) {
    
    //上面的层由下面的层推出
				for(int r=0;r<9;r++) {
    
    
					for(int c=0;c<10;c++) {
    
    
						dp[r][c][h]+=getValue(dp,r-1,c+2,h-1);
						dp[r][c][h]+=getValue(dp,r+1,c+2,h-1);
						dp[r][c][h]+=getValue(dp,r+2,c+1,h-1);
						dp[r][c][h]+=getValue(dp,r+2,c-1,h-1);
						dp[r][c][h]+=getValue(dp,r+1,c-2,h-1);
						dp[r][c][h]+=getValue(dp,r-1,c-2,h-1);
						dp[r][c][h]+=getValue(dp,r-2,c-1,h-1);
						dp[r][c][h]+=getValue(dp,r-2,c+1,h-1);					
					}					
				}
			}
			return dp[x][y][step];	
	}
	public static int getValue(int[][][] dp, int row, int col, int step) {
    
    //给定一个dp表
		if(row<0||row>8||col<0||col>9) {
    
    //解决越界问题,如果越界,就让它累加0
			return 0;
		}
		return dp[row][col][step];//如果不越界,就让它累加
	}
	public static void main(String[] args) {
    
    
		//测试从(0,0)出发走step=10,到达(7,7)的方法数有多少种
		int x=7;
		int y=7;
		int step =10;
		System.out.println(getWays(x,y,step));//一般方法
		System.out.println(dpWays(x,y,step));//动态规划
	}

给定一个行数和列数,表示这个格子有多大

在这里插入图片描述这个人可以往左往右往上往下(各个方向概率相等),越界会死掉,问这个人生存下来的概率是多大
在这里插入图片描述

public class BobLive {
    
    
	//(N,M)指区域的大小,(i,j)指bob目前所在的位置,K是所走的步数
	public static String bob1(int N,int M,int i,int j,int K) {
    
    
		long all=(long) Math.pow(4, K);//先求出总数,不考虑越界死掉的时候,bob可以往上往下往左往右,是4的k次方
		long live=process(N,M,i,j,K);//在算出bob活下来的方法数
		long gcd=gcd(all,live);//求最大公约数
		return String.valueOf(live/gcd)+"/"+(all/gcd);//把分子越掉最大公约数,也把分母约掉最大公约数
	}
	//N*M的区域,Bob从(row,col)位置出发,走rest步之后,获得的生存方方法数
	public static long process(int N, int M, int row, int col, int rest) {
    
    
		if(row<0||row==N||col<0||col==M) {
    
    
			return 0;
		}
		//row,col没越界,又已经走完的情况下 ,活下来了
		if(rest==0) {
    
    
			return 1;
		}
		//还没走完,row,col也没越界
		long live =process(N,M,row-1,col,rest-1);
		live+=process(N,M,row+1,col,rest-1);
		live+=process(N,M,row,col-1,rest-1);
		live+=process(N,M,row,col+11,rest-1);
		
		return live;
	}

	public static long gcd(long m, long n) {
    
    
		return n==0?m:gcd(n,m%n);
	}
	
	//三维尝试做动态规划(做体),二维尝试做动态规划(做表),画出图来,最终想要的位置标出星号,
	//通过basecase决定哪些位置可以直接出答案,分析普遍位置依赖的时候,二维表三维标也是一样的,那个位置先推,那个位置后推,
	//规定好搭积木的顺序,最终把需要的积木块返回
	public static void main(String[] args) {
    
    
		int N=5, M=6, i=2,j=2,K=5;
		System.out.println(bob1(N,M,i,j,K));
	}
}

给定一个数组,每个位置的值代表面值,如【2,3,5,10】,面值类型无重复,,面值张数有任意张,要使用这些面值凑够1000,问方法数有多少种
在这里插入图片描述在这里插入图片描述普通递归解法:

for(int zhang=0;arr[index]*zhang<=rest;zhang++) {
    
    //0位置的面值使用0张的方法数,0位置的面值使用1张的方法数,...
			ways+=process(arr,index+1,rest-arr[index]*zhang);//递归循环,1位置的面值使用0张的方法数,1位置的面值使用1张的方法数...,
		}

完整代码:

	//arr里都是正整数,没有重复值,每个值代表一种货币,每一种都可以使用无限张
	//最终要找零钱数是aim
	//找零方法数返回
	public static  int way1(int[] arr,int aim) {
    
    
		return process(arr,0,aim);
		
	}
	//可以自由使用arr[index...]所有的货币
	//需要搞定的钱数是rest
	//返回找零 的方法数
	public static int process(int[] arr, int index, int rest) {
    
    
		if(index==arr.length) {
    
    
			return rest==0?1:0;
		}
		//arr[index] 0张1张...不超过rest的钱数
		int ways=0;
		for(int zhang=0;arr[index]*zhang<=rest;zhang++) {
    
    //0位置的面值使用0张的方法数,0位置的面值使用1张的方法数,...
			ways+=process(arr,index+1,rest-arr[index]*zhang);//递归循环,1位置的面值使用0张的方法数,1位置的面值使用1张的方法数...,
		}
		return ways;
	}

测试代码:

public static void main(String[] args) {
    
    
		int[] mianZhiArr= {
    
    2,3,5,10};
		int aim=10;
		System.out.println(way1(mianZhiArr,aim));
	}

改为动态规划,让aim=10,数组长度为4,则aim范围是0到10,index是0到4,行列都补充了终止位置,
在这里插入图片描述

		//从左往右,从下往上填
		for(int row=N-1;row>=0;row--) {
    
    //行数从N-1行开始填,
			for(int col=0;col<=aim;col++) {
    
    
				
				int ways=0;
				for(int zhang=0;arr[row]*zhang<=col;zhang++) {
    
    
					ways+=dp[row+1][col-arr[row]*zhang];//调用递归行为改为拿表行为就可以了
					//col是由当前的货币值和选择的张数决定的
				}
				dp[row][col]= ways;//ways最终给当前你要求的格子,即结果数
				
			}
		}

完整代码

	public static int dp1(int[] arr, int aim) {
    
    //arr代表行,index,aim代表列,剩余的钱
		if(arr==null||arr.length==0) {
    
    
			return 0;
		}
		int N=arr.length;
		int[][] dp=new int[N+1][aim+1];//一张dp表
		
		dp[N][0]=1;//最后一行只有0位置是1,其他位置都是0
		//从左往右,从下往上填
		for(int row=N-1;row>=0;row--) {
    
    //行数从N-1行开始填,
			for(int col=0;col<=aim;col++) {
    
    
			
				int ways=0;
				for(int zhang=0;arr[row]*zhang<=col;zhang++) {
    
    
					ways+=dp[row+1][col-arr[row]*zhang];//调用递归行为改为拿表行为就可以了
					//col是由当前的货币值和选择的张数决定的
				}
				dp[row][col]= ways;//ways最终给当前你要求的格子,即结果数
				
			}
		}
		return dp[0][aim];
	}

测试代码

	public static void main(String[] args) {
    
    
		int len=10;
		int max=10;
		int testTime=10000;//对这两个方法测试10000次
		for(int i=0; i<testTime;i++) {
    
    
			int[] arr=genetatrRandomArray(len,max);
			int aim=(int) (Math.random()*3*max)+max;
			
			System.out.println(way1(arr,aim));
			System.out.println(dp1(arr,aim));
			
			if(way1(arr,aim)!=dp1(arr,aim)) {
    
    
				System.out.println("出错了");
				break;
			}
		}
	}

优化想法及代码,所求位置只需要它左边和下边这两个格子,因为左边的额那个格子=b+c+d+…,所求位置的格子=a+b+c+d+…,
在这里插入图片描述

暴力递归下

在这里插入图片描述
有序表的时间复杂度都是O(log(N))级别的–红黑树、AVL、SB、跳表都可以实现有序表,前三个红黑树、AVL(平衡二叉树)、SB是平衡搜索二叉树系列,跳表实单链表改写。
BST是指搜索二叉树
首先要实现搜索二叉树的增删改查(从头结点出发,大的话就往右划,小的话往左划,默认搜索二叉树是没有重复结点的),如果考虑平衡性,就是平衡搜索二叉树。
添加一个结点:
搜索一个结点:
***删除一个结点:***首先搜索该节点是否存在,
1.如果该节点没有左右孩子,让他的父节点指针悬空即可
在这里插入图片描述

2.如果该节点有左右孩子中的一个,让他的孩子替换他的环境即可
在这里插入图片描述

3.如果如果该节点左右孩子都有,如果该节点左右孩子都有,无法判断哪个孩子替换它的环境,可以让左树的最右节点替换他的环境,也可以让右树的最左孩子替换他的环境。
在这里插入图片描述假设是右树,让右树一直往左遍历 ,直到找到没有孩子的结点(当前是4的这个结点)为止,让这个节点来替换要删除的结点。把4的有孩子那一坨(A)给他的父节点6,让后把4放在3的位置(3剥离出去)
在这里插入图片描述在这里插入图片描述当前的搜索二叉树没有平衡性,没有平衡性会出现的问题,时间复杂度没办法维持在log(N)的水平,取决于用户给你的数据状况
log(N)(好情况)
在这里插入图片描述

log(NN)*(坏情况)
在这里插入图片描述
AVL:平衡二叉树,是最严格的平衡性,因为它的任何一个结点的左右子树高度差不超过1,怎样做到(通过左旋和右旋来维持他的平衡性)
在这里插入图片描述这样的话就是带自平衡操作的搜索二叉树(外面包住搜索二叉树的那个域),并不知道左旋右旋怎么用
在这里插入图片描述AVL是在往包一层,首先是搜索二叉树,其次有左旋右旋的操作,AVL实现的是怎么用这个操作
在这里插入图片描述红黑树,首先是搜索二叉树,其次有左旋右旋的操作,有自己关于平衡性的定义,在这种定义下怎么维持这种平衡性
在这里插入图片描述SB树:首先是搜索二叉树,其次有左旋右旋的操作,有自己关于平衡性的定义,在这种定义下怎么维持这种平衡性。怎莫用:有自己的行为操作
在这里插入图片描述现在了解左旋右旋的操作
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
左边少了就左旋,右边少了就右旋,通过左旋右旋可以使树变得平衡一点
在这里插入图片描述破坏平衡的4中类型:LL,RR,LR,RL
LL 型,左边孩子过长,导致的平衡性失效,这样的话,做一个右旋就行
在这里插入图片描述RR,右边孩子过长,导致的平衡性失效,这样的话,做一个左旋就行
在这里插入图片描述LR型,左树的右孩子过长,(解决办法:想办法让X变成头部)
在这里插入图片描述1.先让Y这棵树左旋,让X上去
在这里插入图片描述2.再让Z这棵树右旋,让X上去
在这里插入图片描述RL型,是右树的左孩子过长,(想办法让C成为头部)
在这里插入图片描述1.让B这棵树右旋,使C上去
2.再让Z这棵树左旋,使C上去

SetsBalance树(SB树)定义:任意一个结点都不比他侄子结点小,跟AVL树一样也有4种类型《LL,RR,LR,RL》

在这里插入图片描述

红黑树:1点要么是黑要么是红,
2.头和叶子结点是黑的,
3.红黑结点不相邻,
4.cur是当前头结点,每条到结束结点的路中黑色结点一样多

跳表(我不懂):是实现有序表的一种方式,用户随意给数据,

在这里插入图片描述
在这里插入图片描述现在假设要加入70,有2层,永远从最高层开始,找到<=70最右的结点,所以来到20

在这里插入图片描述70没有5层结点,20自己内部来到4层,在4层上要找到<=70最右的结点,就往右来到了50

在这里插入图片描述70没有4层,在50内部跳转到3层。70也没有3层,所以在50内部跳转到2层。70也没有2层,所以在50内部跳转到1层。有1层,所以挂上去,把指针指向70
在这里插入图片描述效果就是50结点中第0层和第1层会指向70。70结点中第0层和第1层会指向100

3个月到3年

猜你喜欢

转载自blog.csdn.net/qq_42373007/article/details/123958217