暴力递归
暴力递归就是尝试
- 把问题转化为规模缩小了的同类问题的子问题
- 有明确的不需要继续进行递归的条件(base case)
- 有当得到了子问题的结果之后的决策过程
- 不记录每一个子问题的解
一定要学会怎么去尝试,因为这是动态规划的基础
汉诺塔问题
打印n层汉诺塔从最左边移动到最右边的全部过程
public class Hanoi {
public static void hanoi(int n){
if(n > 0){
func(n,"左","右","中");
}
}
//1~i 圆盘目标是from->to other是另一个
public static void func(int i, String start, String end, String other){
//base
if(i == 1){
System.out.println("move 1 from " + start+ " to " + end);
}else {
func(i-1, start,other,end);
System.out.println("move " + i + " from " + start+ " to " + end);
func(i-1,other,end,start);
}
}
public static void main(String[] args) {
hanoi(3);
}
}
打印一个字符串的全部子序列,包括空字符串
public class PrintAllSubsequences {
public static void printAllSubsequences (String str){
char[] chs = str.toCharArray();
process(chs,0);
}
//当前来到i位置,要和不要,走两条路
//之前的选择,所形成的结果,是str
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;
//不要当前字符的路
process(chs,i+1);
chs[i] = tmp;
}
public static void main(String[] args) {
String test = "abc";
printAllSubsequences (test.toCharArray(),0,"");
}
//===================================
public static void function(String str){
char[] chs = str.toCharArray();
process(chs,0,new ArrayList<Character>());
}
//当前来到i位置,要和不要,走两条路
//res之前的选择,所形成的列表
public static void process(char[] str,int i,List<Character> res){
if(i == str.length){
printList(res);
return;
}
List<Character> resKeep = copyList(res);
resKeep.add(str[i]);
//要当前字符的路
process(str,i+1,resKeep);
//不要当前字符的路
List<Character> resNoInclude = copyList(res);
process(str,i+1,resNoInclude);
}
public static void printList(List<Character> res){
//自己补充
}
public static List<Character> copyList(List<Character> list)
{
//自己补充
return null;
}
}
打印一个字符串的全部排列
public class FullPermutation {
public static void main(String[] args) {
String str="abc";
char[] c=str.toCharArray();
List<String> list=new ArrayList<>();
process(c,0, list);
System.out.println(list);
}
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[i..]范围上,所有的字符,都可以在i位置上,后续都去尝试
//str[0..i-1]范围上,是之前做的选择
//把所有字符串形成的全排列,加入到res去
public static void process(char[] str, int i, List<String> res){
if (i == str.length){
res.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,res);
swap(str,i,j);
}
}
}
private static void swap(char[] str, int k, int i) {
char c=str[k];
str[k]=str[i];
str[i]=c;
}
}
拿牌问题
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));
}
}
逆序栈问题
给你一个栈,请你逆序这个栈,不能申请额外的数据结构,只能使用递归函数,如何实现
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;
}
}
字母和数字对应问题
规定1和A对应,2和B对应,3和C对应。。
那么一个数字字符串比如“111”,就可以转化为“AAA”,“KA”,和“AK”
给定一个只有数字字符组成的字符串str,返回有多少种转化结果
// i 之前的位置,如何转化已做过决定了
// i... 有多少种转化结果
public static int process(char[] str,int i){
if(i == str.length){
return 1;
}
if(str[i] == '0'){
return 0;
}
if(str[i] == '1'){
//i作为单独的部分,后续有多少种方法
int res = process(str,i+1);
//i和i+1 一起作为一部分,后续有多少种方法
if(i + 1 < str.length){
res += process(str,i+2);
}
return res;
}
if(str[i] == '2'){
//i作为单独的部分,后续有多少种方法
int res = process(str,i + 1);
//i和i+1 一起作为一部分,并且没有超过26,后续有多少种方法
if(i + 1 < str.length && str[i + 1] >= '0' && str[i + 1] <= '6')){
res += process(str,i + 2);
}
return res;
}
//当前字符是3~9
return process(str,i + 1);
}
背包问题
给定两个长度都为N的数组weight和values,weight[i]和values[i]分别表示i号物品的重量和价值。给定一个正数bag,表示一个载重bag的袋子,你装的物品不能超过这个重量,返回你能装下的最多的价值是多少
// i..的货物自由选择,形成的最大价值返回
// 重量不要超过bag
// 之前做过的决定所形成的重量,alreadyweight
public static int process1(int[] weight,int[] values,int i,int alreadyweight,int bag){
if(alreadyweight > bag){
return 0;
}
if(i == weight.lenght){
return 0;
}
return Math.max(
process1(
// 不要i号货物
weight,values,i + 1,alreadyweight,bag),
//要i号货物
values[i] + process1(weight,values,i + 1,alreadyweight + weight[i],bag)
);
}