暴力递归就是尝试

  1. 把问题转化为规模缩小了的同类问题的子问题
  2. 有明确的不需要继续进行递归的条件(base case)
  3. 有当得到了子问题的结果之后的决策过程
  4. 不记录每一个子问题的解
  5. 一定要学会怎么去尝试,因为这是动态规划的基础

    汉诺塔问题

    打印n层汉诺塔从最左边移动到最右边的全部过程

  1. public class Code01_Hanoi {
  2. public static void hanoi(int n) {
  3. if (n > 0) {
  4. func(n, n, "left", "mid", "right");
  5. }
  6. }
  7. public static void func(int rest, int down, String from, String help, String to) {
  8. if (rest == 1) {
  9. System.out.println("move " + down + " from " + from + " to " + to);
  10. } else {
  11. func(rest - 1, down - 1, from, to, help);
  12. func(1, down, from, help, to);
  13. func(rest - 1, down - 1, help, from, to);
  14. }
  15. }
  16. public static void main(String[] args) {
  17. int n = 3;
  18. hanoi(n);
  19. }
  20. }

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

image.png

  1. public class PrintAllSubsquences {
  2. public static void printAllSubsquence(String str) {
  3. char[] chs = str.toCharArray();
  4. process(chs, 0);
  5. }
  6. // 当前来到i位置,要和不要,走两条路
  7. // 之前的选择,所形成的结果,是str
  8. public static void process(char[] chs, int i) {
  9. if (i == chs.length) {
  10. System.out.println(String.valueOf(chs));
  11. return;
  12. }
  13. process(chs, i + 1);
  14. char tmp = chs[i];
  15. chs[i] = 0;
  16. process(chs, i + 1);
  17. chs[i] = tmp;
  18. }
  19. public static void function(String str) {
  20. char[] chs = str.toCharArray();
  21. process(chs, 0, new ArrayList<Character>());
  22. }
  23. // 当前来到i位置,要和不要,走两条路
  24. // res之前的选择,所形成的列表
  25. public static void process(char[] chs, int i, List<Character> res) {
  26. if(i == chs.length) {
  27. printList(res);
  28. }
  29. List<Character> resKeep = copyList(res);
  30. resKeep.add(chs[i]);
  31. process(chs, i+1, resKeep); // 要当前字符的路
  32. List<Character> resNoInclude = copyList(res);
  33. process(chs, i+1, resNoInclude); // 不要当前字符的路
  34. }
  35. public static void printList(List<Character> res) {
  36. // ...;
  37. }
  38. public static List<Character> copyList(List<Character> list){
  39. return null;
  40. }
  41. public static void main(String[] args) {
  42. String test = "abc";
  43. printAllSubsquence(test);
  44. }
  45. }

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

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

  1. public class PrintAllPermutations {
  2. public static ArrayList<String> Permutation(String str) {
  3. ArrayList<String> res = new ArrayList<>();
  4. if (str == null || str.length() == 0) {
  5. return res;
  6. }
  7. char[] chs = str.toCharArray();
  8. process(chs, 0, res);
  9. res.sort(null);
  10. return res;
  11. }
  12. public static void process(char[] chs, int i, ArrayList<String> res) {
  13. if (i == chs.length) {
  14. res.add(String.valueOf(chs));
  15. }
  16. boolean[] visit = new boolean[26]; // 注释掉
  17. for (int j = i; j < chs.length; j++) {
  18. // 这叫分支限界,提前杀死不可能走的路
  19. if (!visit[chs[j] - 'a']) { // 注释掉 就是没有重复的排列
  20. visit[chs[j] - 'a'] = true; // 注释掉
  21. swap(chs, i, j);
  22. process(chs, i + 1, res);
  23. swap(chs, i, j);
  24. }
  25. }
  26. }
  27. public static void swap(char[] chs, int i, int j) {
  28. char tmp = chs[i];
  29. chs[i] = chs[j];
  30. chs[j] = tmp;
  31. }
  32. }

取牌游戏

image.png

  1. public class CardsInLine {
  2. public static int win1(int[] arr) {
  3. if (arr == null || arr.length == 0) {
  4. return 0;
  5. }
  6. return Math.max(f(arr, 0, arr.length - 1), s(arr, 0, arr.length - 1));
  7. }
  8. public static int f(int[] arr, int i, int j) {
  9. if (i == j) {
  10. return arr[i];
  11. }
  12. return Math.max(arr[i] + s(arr, i + 1, j), arr[j] + s(arr, i, j - 1));
  13. }
  14. public static int s(int[] arr, int i, int j) {
  15. if (i == j) {
  16. return 0;
  17. }
  18. return Math.min(f(arr, i + 1, j), f(arr, i, j - 1));
  19. }
  20. // 动态规划
  21. public static int win2(int[] arr) {
  22. if (arr == null || arr.length == 0) {
  23. return 0;
  24. }
  25. int[][] f = new int[arr.length][arr.length];
  26. int[][] s = new int[arr.length][arr.length];
  27. for (int j = 0; j < arr.length; j++) {
  28. f[j][j] = arr[j];
  29. for (int i = j - 1; i >= 0; i--) {
  30. f[i][j] = Math.max(arr[i] + s[i + 1][j], arr[j] + s[i][j - 1]);
  31. s[i][j] = Math.min(f[i + 1][j], f[i][j - 1]);
  32. }
  33. }
  34. return Math.max(f[0][arr.length - 1], s[0][arr.length - 1]);
  35. }
  36. public static void main(String[] args) {
  37. int[] arr = { 1, 9, 1 };
  38. System.out.println(win1(arr));
  39. System.out.println(win2(arr));
  40. }
  41. }

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

public class 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());
        }
    }
}

规定1和A对应、2和B对应、3和C对应、、、,那么一个数字字符串比如“111”,就可以转化为“AAA”、“KA”和“AK”。给定一个只有数字字符组成的字符串str,返回有多少种转化结果。

public class ConvertToLetterString {

    public static int number(String str) {
        if (str == null || str.length() == 0) {
            return 0;
        }
        return process(str.toCharArray(), 0);
    }

    // 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); //(i和i+1)作为单独的部分,后续有多少种方法
            }
            return res;
        }
        // str[i] == '3' ~ '9'
        return process(chs, i + 1);
    }

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

给定两个长度都为N的数组weights和values,weights[i]和values[i]分别代表i号物品的重量和价值。给定一个正数bag,表示一个载重bag的袋子,你装的物品不能超过这个重量。返回你能装下最多的价值是多少?

public class 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(
            // 不要当前i号货物的价值
            process1(weights, values, i + 1, alreadyweight, bag),
            // 要当前i号货物的价值
            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));
    }
}