暴力递归

本文介绍了暴力递归的概念,并通过汉诺塔问题、逆序栈实现、并查集合并用户以及字符串子序列的打印算法进行了详细阐述。示例代码涵盖了Java实现,包括打印n层汉诺塔的全过程、使用递归反转栈、处理用户合并以及打印字符串的所有子序列(包括不重复的子序列)和排列。这些例子展示了递归和数据结构在解决复杂问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

暴力递归

思想:

  • 暴力递归就是尝试
    • 把问题转化为规模缩小的同类问题的子问题
    • 有明确的不需要进行继续递归的条件
    • 有当得到了子问题结果之后的决策过程
    • 不记录每个子问题的解

记录每个问题的解是动态规划,不记录每个过程的解是暴力递归

汉诺塔

打印n层汉诺塔从最左边移动到最右边的全部过程
假设数字为3个

  • 项目
    • 1,2左->中间
      • 1左->右
      • 2左->中间
      • 1右->中间
    • 3左->右边
    • 1,2中间->右边
      • 1中间->左边
      • 2中间->右边
      • 1左边-右边

代码实现如下:

package Digui;
public class prim1 {
	public static void hao(int n){
		if (n>0) {
			fun(n,"left","right","mid");
		}
	}
	public static void fun(int N, String from,String to, String other){
		if (N==1) {
			System.out.println("move 1 from"+" "+from+" "+"to"+" "+to);	
		}
		else {
			fun(N-1, from, other, to);
			System.out.println("move"+ N +"from"+" "+from+" "+"to"+" "+to);
			fun(N-1, other, to, from);
		}
	}
	public static void main(String[] args) {
		int n=3;
		hao(n);
	}
}
/*move 1 from left to right
move2from left to mid
move 1 from right to mid
move3from left to right
move 1 from mid to left
move2from mid to right
move 1 from left to right*/

逆序栈

给你一个栈,请你逆序这个栈,不能申请额外的数据结构,只能使用递归函数

给123的栈,
把栈的元素去到底,并返回
在这里插入图片描述
在这里插入图片描述代码如下:

package Digui;
import java.util.Stack;
public class ReverseStack {
	public static void Reverse(Stack<Integer> stack){
		if (stack.isEmpty()) {
			return;
		}
		int i=fun(stack);
		Reverse(stack);
		stack.push(i);
	}
	public static int fun(Stack<Integer> stack){
		int result=stack.pop();
		if(stack.isEmpty()){
			return result;
		}else {
			int last=fun(stack);
			stack.push(result);
			return last;
		}
	}
	public static void main(String[] args) {
		Stack<Integer> test=new Stack<>();
		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.print(test.pop()+" ");
		}	
	}
}

并查集合并user

如果两个user,a字段一样,或者b字段一样,或者c字段一样,就认为是一个人,请合并user,返回合并之后的用户数量

package Digui;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
public class Mergeusers {
	public static class Node<V> {
		V value;

		public Node(V v) {
			value = v;
		}
	}
	public static class UnionFind<V> {
		public HashMap<V, Node<V>> nodes = new HashMap<>();
		public HashMap<Node<V>, Node<V>> parents = new HashMap<>();
		public HashMap<Node<V>, Integer> sizeMap = new HashMap<>();

		public UnionFind(List<V> values){
			for(V cur:values){
				Node<V> node = new Node<>(cur);
				nodes.put(cur, node);
				parents.put(node, node);
				sizeMap.put(node, 1);
			}
		}

		// 给你一个节点,请你往上到不能再往上,把代表返回
		public Node<V> findFather(Node<V> cur) {
			Stack<Node<V>> path = new Stack<>();
			while (cur != parents.get(cur)) {
				path.push(cur);
				cur = parents.get(cur);
			}
			while (!path.isEmpty()) {
				parents.put(path.pop(), cur);
			}
			return cur;
		}
		public boolean isSameSet(V a, V b) {
			return findFather(nodes.get(a)) == findFather(nodes.get(b));
		}
		public void union(V a, V b) {
			Node<V> aHead = findFather(nodes.get(a));
			Node<V> bHead = findFather(nodes.get(b));
			if (aHead != bHead) {
				int aSetSize = sizeMap.get(aHead);
				int bSetSize = sizeMap.get(bHead);
				Node<V> big = aSetSize >= bSetSize ? aHead : bHead;
				Node<V> small = big == aHead ? bHead : aHead;
				parents.put(small, big);
				sizeMap.put(big, aSetSize + bSetSize);
				sizeMap.remove(small);
			}
		}
		public int sets() {
			return sizeMap.size();
		}
	}
	public static class  User{
		public String a;
		public String b;
		public String c;
		
		public User(String a,String b,String c){
			this.a=a;
			this.b=b;
			this.c=c;
		}
		@Override
		public String toString(){
			return "a:" + a + " b:" + b + " c:" + c;
		}	
	}
	public static int mergeuse(List<User> users){
		UnionFind<User> userfind=new UnionFind<>(users);
		HashMap<String, User> mapA=new HashMap<>();
		HashMap<String, User> mapB=new HashMap<>();
		HashMap<String, User> mapC=new HashMap<>();
		for(User user:users){
			if (mapA.containsKey(user.a)) {
				userfind.union(user, mapA.get(user.a));
			}
			else{
				mapA.put(user.a, user);
			}
			if (mapB.containsKey(user.b)) {
				userfind.union(user, mapB.get(user.b));
			}
			else{
				mapB.put(user.b, user);
			}
			if (mapC.containsKey(user.c)) {
				userfind.union(user, mapC.get(user.c));
			}
			else{
				mapC.put(user.c, user);
			}
		}
		//向并差集询问还有多少user
		return userfind.sets();
	}
	public static void main(String[] args) {
		User user1=new User("1","10","13");
		User user2=new User("2","10","37");
		User user3=new User("100","200","37");
		List<User> users=new ArrayList<>();
		users.add(user1);
		users.add(user2);
		users.add(user3);
		for(User user:users){
			System.out.println(user);
		}
		System.out.print(mergeuse(users));
	} 
}

打印字符串子序列

打印一个字符串的全部子序列
子序列:把一个位置的字符彻底地要或是不要

package Digui;
import java.util.List;
import java.util.ArrayList;
public class PrintAllSubsquences {
	public static List<String> subs(String s){
		char[] str=s.toCharArray();
		String path=" ";
		List<String> ans=new ArrayList<>();
		process1(str, 0, ans, path);
		return ans;
	}
	//str固定,不变
	//index此时来到的位置,要或者不要
	//如果index来到str的终止位置,将沿途路径所形成的答案放入ans中
	//之前作出的选择就是path
	public static void process1(char[] s,int index,List<String> ans,String path){
		if (index==s.length) {
			ans.add(path);
			return;
		}
		String no=path;
		process1(s, index+1, ans, no);
		
		String yes=path+String.valueOf(s[index]);
		process1(s, index+1, ans, yes);
		
	}
	public static void main(String[] args) {
		String s="abcd";
		System.out.println(subs(s));
	}
}
//[ ,  d,  c,  cd,  b,  bd,  bc,  bcd,  a,  ad,  ac,  acd,  ab,  abd,  abc,  abcd]

打印字符串子序列(不重复)

打印一个字符串的全部子序列,要求不要出现重复字面值的子序列

package Digui;
import java.util.List;
import java.util.ArrayList;
import java.util.HashSet;
public class PrintAllSubsquences1 {
		public static List<String> SubNoRepeat(String s){
			char[] str=s.toCharArray();
			String path=" ";
			HashSet<String> ans=new HashSet<>();
			process2(str, 0, ans, path);
			List<String> ans1=new ArrayList<>();
			for(String cur:ans){
				ans1.add(cur);
			}
			return ans1;
		}
		//str固定,不变
		//index此时来到的位置,要或者不要
		//如果index来到str的终止位置,将沿途路径所形成的答案放入ans中
		//之前作出的选择就是path
		public static void process2(char[] s,int index,HashSet<String> ans,String path){
			if (index==s.length) {
				ans.add(path);
				return;
			}
			String no=path;
			process2(s, index+1, ans, no);
			
			String yes=path+String.valueOf(s[index]);
			process2(s, index+1, ans, yes);
			
		}
		public static void main(String[] args) {
			String s="aaa";
			System.out.println(SubNoRepeat(s));
		}
	}
//[ ,  aa,  a,  aaa]

打印字符串全部排列

打印一个字符串的全部排列

package Digui;
import java.util.ArrayList;
public class printallPermutation {
	public static ArrayList<String> Permutation(String str){
		ArrayList<String> res=new ArrayList<>();
		if(str==null||str.length()==0){
			return res;
		}
		char[] chs=str.toCharArray();
		process(chs, 0, res);
		return res;
	}
	//str[0...i]已经是做好决定的
	//str[i...]都有机会来到i位置
	//i终止位置,就是str当前样子,就是一种结果->ans
	public static void process(char[] str,int i,ArrayList<String>ans ){
		if (i==str.length) {
			ans.add(String.valueOf(str));
		}
		for (int j = i; j < str.length; j++) {
			swap(str,i,j);
			process(str, i+1, ans);
			swap(str,i,j);
		}
	}
	public static void swap(char[] arr,int i,int j){
		char temp=arr[i];
		arr[i]=arr[j];
		arr[j]=temp;
	}
	public static void main(String[] args) {
		String s="123";
		System.out.println(Permutation(s));
	}
}
//[123, 132, 213, 231, 321, 312]

打印字符串全部排列(不重复)

打印一个字符串的全部排列

package Digui;
import java.util.HashSet;
public class printallPermutationNorepeat {
	public static HashSet<String> Permutation(String str){
		HashSet<String> res=new HashSet<>();
		if(str==null||str.length()==0){
			return res;
		}
		char[] chs=str.toCharArray();
		process(chs, 0, res);
		return res;
	}
	//str[0...i]已经是做好决定的
	//str[i...]都有机会来到i位置
	//i终止位置,就是str当前样子,就是一种结果->ans
	public static void process(char[] str,int i,HashSet<String>ans ){
		if (i==str.length) {
			ans.add(String.valueOf(str));
		}
		for (int j = i; j < str.length; j++) {
			swap(str,i,j);
			process(str, i+1, ans);
			swap(str,i,j);
		}
	}
	public static void swap(char[] arr,int i,int j){
		char temp=arr[i];
		arr[i]=arr[j];
		arr[j]=temp;
	}
	public static void main(String[] args) {
		String s="1233";
		System.out.println(Permutation(s));
		//[1233, 1233, 1323, 1332, 1332, 1323, 2133, 2133, 2313, 2331, 2331, 2313, 3213, 
		//3231, 3123, 3132, 3312, 3321, 3231, 3213, 3321, 3312, 3132, 3123] 
		
		//不重复的结果
		//[1323, 2313, 1233, 1332, 3213, 3312, 2133, 2331, 3123, 3321, 3231, 3132]
	}
}

另外一种版本

package Digui;
import java.util.ArrayList;
public class printallPermutationNorepeat1 {
	public static ArrayList<String> Permutation(String str){
		ArrayList<String> res=new ArrayList<>();
		if(str==null||str.length()==0){
			return res;
		}
		char[] chs=str.toCharArray();
		process(chs, 0, res);
		return res;
	}
	//str[0...i]已经是做好决定的
	//str[i...]都有机会来到i位置
	//i终止位置,就是str当前样子,就是一种结果->ans
	public static void process(char[] str,int i,ArrayList<String>ans ){
		if (i==str.length) {
			ans.add(String.valueOf(str));
		}
		boolean[] visit=new boolean[26];
		for (int j = i; j < str.length; j++) {
			if (!visit[str[j]-'a']) {
				visit[str[j]-'a']=true;
				swap(str,i,j);
				process(str, i+1, ans);
				swap(str,i,j);
			}
		}
	}
	public static void swap(char[] arr,int i,int j){
		char temp=arr[i];
		arr[i]=arr[j];
		arr[j]=temp;
	}
	public static void main(String[] args) {
		String s="aaa";
		System.out.println(Permutation(s));
	}
}

### Java 实现从暴力递归动态规划的优化 #### 暴力递归实现 对于某些问题,如零钱兑换问题,在最初阶段可以采用暴力递归的方法解决。然而这种方法存在大量重复计算的问题,效率低下。 ```java public int coinChange(int[] coins, int amount) { if (amount < 0) { return -1; } if (amount == 0) { return 0; } int minCount = Integer.MAX_VALUE; for (int i = 0; i < coins.length; i++) { int res = coinChange(coins, amount - coins[i]); if (res >= 0 && res < minCount) { minCount = 1 + res; } } return minCount == Integer.MAX_VALUE ? -1 : minCount; } ``` 此代码展示了最基础的递归逻辑[^1]。当金额 `amount` 小于零返回 `-1` 表示无法组合;等于零表示正好匹配成功,不需要任何硬币参与运算。遍历每种面额尝试减少当前总金额并递归求解剩余部分所需的最少数量直到找到最小值或者确认无解为止。 #### 添加记忆化搜索(Memoization) 为了改善上述算法性能,可以在原有基础上引入缓存机制保存已经计算过的结果防止不必要的重复工作: ```java private static final Map<Integer, Integer> memo = new HashMap<>(); public int coinChangeWithMemo(int[] coins, int amount) { if (amount < 0) { return -1; } if (amount == 0) { return 0; } // Check cache first before computing. if (!memo.containsKey(amount)) { int minCount = Integer.MAX_VALUE; for (int i = 0; i < coins.length; ++i) { int res = coinChangeWithMemo(coins, amount - coins[i]); if (res >= 0 && res < minCount) { minCount = 1 + res; } } memo.put(amount, (minCount == Integer.MAX_VALUE) ? -1 : minCount); } return memo.get(amount); } ``` 这段改进后的版本利用哈希表作为外部存储器记录之前遇到过的输入及其对应输出以便快速检索已知答案从而大大减少了时间复杂度[^3]。 #### 完全转换成自底向上的动态规划方案 最终目标是从完全依赖函数调用来构建表格形式的数据结构逐步填充直至获得全局最优解的过程称为“自底向上”。这种方式不仅消除了显式的栈空间消耗还进一步提高了程序运行速度。 ```java public int dpCoinChange(int[] coins, int amount) { int max = amount + 1; int[] dp = new int[max]; Arrays.fill(dp, max); dp[0] = 0; for (int i = 1; i <= amount; i++) { for (int j = 0; j < coins.length; j++) { if (coins[j] <= i) { dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1); } } } return dp[amount] > amount ? -1 : dp[amount]; } ``` 这里定义了一个长度为 `max` 的数组 `dp` 来保存不同数额所需最少硬币数目,并初始化所有元素为最大整数值代表未知状态。接着按照从小到大顺序更新每一个位置处的最佳选择结果直到处理完毕整个范围内的数据项。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值