算法与数据结构笔记四(暴力递归)

本文主要探讨了暴力递归的概念及其在解决汉诺塔问题、字符串子序列与排列、纸牌游戏、逆序栈、字符串转化和装袋问题等经典算法中的应用。通过递归策略解决问题,并强调了动态规划和剪枝的重要性。

8. 暴力递归

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

一定要学会怎么去尝试,因为这是动态规划的基础,这一内容我们将在提升班讲述

8.1 汉诺塔问题

0:45:0
三个杆,大的不能压小的,
在这里插入图片描述
在这里插入图片描述

package class08;

public class Code01_Hanoi {

	public static void hanoi(int n) {
		if (n > 0) {
			func(n, n, "left", "mid", "right");
		}
	}
	// 1~i  圆盘 目标是from -》 to , other是另外一个
	public static void func(int rest, int down, String from, String help, String to) {
		if (rest == 1) {// base case
			System.out.println("move " + down + " from " + from + " to " + to);
		} else {
			func(rest - 1, down - 1, from, to, help);
			func(1, down, from, help, to);
			func(rest - 1, down - 1, help, from, to);
		}
	}

	public static void main(String[] args) {
		int n = 3;
		hanoi(n);
	}

}

8.2 打印全部子序列

打印一个字符串的全部子序列,包括空字符串

在这里插入图片描述

package class08;

import java.util.ArrayList;
import java.util.List;

public class Code02_PrintAllSubsquences {

	public static void printAllSubsquence(String str) {
		char[] chs = str.toCharArray();
		process(chs, 0);
	}


	// 当前来到i位置,要和不要,走两条路
	// 之前的选择,所形成的结果   是chs
	public static void process(char[] chs, int i) {
		if (i == chs.length) {
			System.out.println(String.valueOf(chs));
			return;
		}
		process(chs, i + 1);
		char tmp = chs[i];
		chs[i] = 0; // char数组  放入零, asic吗没有输出值
		process(chs, i + 1);
		chs[i] = tmp;
	}
	
	public static void function(String str) {
		char[] chs = str.toCharArray();
		process(chs, 0, new ArrayList<Character>());
	}
	
	// 当前来到i位置,要和不要,走两条路
	// res 之前的选择,所形成的列表
	public static void process(char[] chs, int i, List<Character> res) {
		if(i == chs.length) {
			printList(res);
		}
		List<Character> resKeep = copyList(res);
		resKeep.add(chs[i]);
		process(chs, i+1, resKeep); // 要当前字符的路
		List<Character> resNoInclude = copyList(res);
		process(chs, i+1, resNoInclude);// 不要当前字符的路
	}
	
	public static void printList(List<Character> res) {
		// ...;
	}
	
	public static List<Character> copyList(List<Character> list){
		return null;
	}
	

	public static void main(String[] args) {
		String test = "abc";
		printAllSubsquence(test);
	}

}

8.3 打印字符串的全部排列

leetcode46
打印一个字符串的全部排列
打印一个字符串的全部排列,要求不要出现重复的排列

在这里插入图片描述

回溯就是在递归的基础之上在每一个步骤上进行标记和取消标记的处理

package class08;

import java.util.ArrayList;

public class Code03_PrintAllPermutations {

	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);
		res.sort(null);
		return res;
	}

	// str[i...] 范围上, 所有的字符,都可以在i位置上 ,后续都去尝试
	// str[0...i-1] 范围上,是之前做的选择
	// 请把所有的字符串形成的全排列,加到res上去
	public static void process(char[] chs, int i, ArrayList<String> res) {
		if (i == chs.length) {
			res.add(String.valueOf(chs));
		}
		//boolean[] visit = new boolean[26];
		for (int j = i; j < chs.length; j++) {
//		if (!visit[chs[j] - 'a']) {
//				visit[chs[j] - 'a'] = true;
				swap(chs, i, j);
				process(chs, i + 1, res);
				swap(chs, i, j);
//			}
		}
	}

	public static void swap(char[] chs, int i, int j) {
		char tmp = chs[i];
		chs[i] = chs[j];
		chs[j] = tmp;
	}

}

8.4 打印字符串的不重复全部排列

分支限界。剪枝。
不重复

package class08;

import java.util.ArrayList;

public class Code03_PrintAllPermutations {

	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);
		res.sort(null);
		return res;
	}

	// str[i...] 范围上, 所有的字符,都可以在i位置上 ,后续都去尝试
	// str[0...i-1] 范围上,是之前做的选择
	// 请把所有的字符串形成的全排列,加到res上去
	public static void process(char[] chs, int i, ArrayList<String> res) {
		if (i == chs.length) {
			res.add(String.valueOf(chs));
		}
		boolean[] visit = new boolean[26];  // 试过还是没试过
		for (int j = i; j < chs.length; j++) {
		if (!visit[chs[j] - 'a']) {//没试过 就试
				visit[chs[j] - 'a'] = true;
				swap(chs, i, j);
				process(chs, i + 1, res);
				swap(chs, i, j);
//			}
		}
	}

	public static void swap(char[] chs, int i, int j) {
		char tmp = chs[i];
		chs[i] = chs[j];
		chs[j] = tmp;
	}

}

8.5 纸牌

在这里插入图片描述
在这里插入图片描述
后手函数
在这里插入图片描述

package class08;

public class Code08_CardsInLine {

	public static int win1(int[] arr) {
		if (arr == null || arr.length == 0) {
			return 0;
		}
		return Math.max(f(arr, 0, arr.length - 1), s(arr, 0, arr.length - 1));
	}

	public static int f(int[] arr, int i, int j) {
		if (i == j) {
			return arr[i];
		}
		return Math.max(arr[i] + s(arr, i + 1, j), arr[j] + s(arr, i, j - 1));
	}

	public static int s(int[] arr, int i, int j) {
		if (i == j) {
			return 0;
		}
		return Math.min(f(arr, i + 1, j), f(arr, i, j - 1));
	}

	public static int win2(int[] arr) {
		if (arr == null || arr.length == 0) {
			return 0;
		}
		int[][] f = new int[arr.length][arr.length];
		int[][] s = new int[arr.length][arr.length];
		for (int j = 0; j < arr.length; j++) {
			f[j][j] = arr[j];
			for (int i = j - 1; i >= 0; i--) {
				f[i][j] = Math.max(arr[i] + s[i + 1][j], arr[j] + s[i][j - 1]);
				s[i][j] = Math.min(f[i + 1][j], f[i][j - 1]);
			}
		}
		return Math.max(f[0][arr.length - 1], s[0][arr.length - 1]);
	}

	public static void main(String[] args) {
		int[] arr = { 1, 9, 1 };
		System.out.println(win1(arr));
		System.out.println(win2(arr));

	}

}

8.6 逆序栈

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

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

package class08;

import java.util.Stack;

public class Code04_ReverseStackUsingRecursive {

	public static void reverse(Stack<Integer> stack) {
		if (stack.isEmpty()) {
			return;
		}
		int i = getAndRemoveLastElement(stack);
		reverse(stack);
		stack.push(i);
	}

	public static int getAndRemoveLastElement(Stack<Integer> stack) {
		int result = stack.pop();
		if (stack.isEmpty()) {
			return result;
		} else {
			int last = getAndRemoveLastElement(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);
		reverse(test);
		while (!test.isEmpty()) {
			System.out.println(test.pop());
		}

	}

}

8.7 字符串转化

规定1和A对应、2和B对应、3和C对应…
那么一个数字字符串比如“111",就可以转化为“AAA”、"KA”和"AK”。
给定一个只有数字字符组成的字符串str,返回有多少种转化结果。
在这里插入图片描述
1:47:00

package class08;

public class Code06_ConvertToLetterString {

	public static int number(String str) {
		if (str == null || str.length() == 0) {
			return 0;
		}
		return process(str.toCharArray(), 0);
	}
	
	// i之前的位置,如何转化已经做过决定了
	// i..  有多少种转化结果
	public static int process(char[] chs, int i) {
		if (i == chs.length) {
			return 1;
		}
		if (chs[i] == '0') {
			return 0;
		}
		if (chs[i] == '1') {
			int res = process(chs, i + 1);  // i自己作为单独的部分,后续有多少种方法
			if (i + 1 < chs.length) {
				res += process(chs, i + 2);// (i和i+1)作为单独的部分,后续有多少种方法
			}
			return res;
		}
		if (chs[i] == '2') {
			int res = process(chs, i + 1);// i自己作为单独的部分,后续有多少种方法
			// (i和i+1)作为单独的部分并且没有超过26,后续有多少种方法
			if (i + 1 < chs.length && (chs[i + 1] >= '0' && chs[i + 1] <= '6')) {
				res += process(chs, i + 2);
			}
			return res;
		}
		//chs[i] == '3'~'9'
		return process(chs, i + 1);
	}

	public static void main(String[] args) {
		System.out.println(number("11111"));
	}

}

8.8 装袋问题

在这里插入图片描述

package class08;

public class Code07_Knapsack {

	public static int maxValue1(int[] weights, int[] values, int bag) {
		return process1(weights, values, 0, 0, bag);
	}

	// i... 的货物选择, 形成最大的价值返回
	// 重量永远不会超过bag
	// 之前做的决定,所达到的重量, alreadyweight
	public static int process1(int[] weights, int[] values, int i, int alreadyweight, int bag) {
		if (alreadyweight > bag) {
			return 0;
		}
		if (i == weights.length) {
			return 0;
		}
		return Math.max(

				process1(weights, values, i + 1, alreadyweight, bag),

				values[i] + process1(weights, values, i + 1, alreadyweight + weights[i], bag));
	}

	public static int maxValue2(int[] c, int[] p, int bag) {
		int[][] dp = new int[c.length + 1][bag + 1];
		for (int i = c.length - 1; i >= 0; i--) {
			for (int j = bag; j >= 0; j--) {
				dp[i][j] = dp[i + 1][j];
				if (j + c[i] <= bag) {
					dp[i][j] = Math.max(dp[i][j], p[i] + dp[i + 1][j + c[i]]);
				}
			}
		}
		return dp[0][0];
	}

	public static void main(String[] args) {
		int[] weights = { 3, 2, 4, 7 };
		int[] values = { 5, 6, 3, 19 };
		int bag = 11;
		System.out.println(maxValue1(weights, values, bag));
		System.out.println(maxValue2(weights, values, bag));
	}

}

总结

基础阶段结束,然后把前面的代码全部自己打一边,并在leetcode上跑通。并在有些经典代码在python上也实现下,准备下蓝桥杯。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值