算法基础入门——汉诺塔问题、字符串的子序列、字符串的全排列、纸牌赢家问题、递归逆序栈、数字转化字符串问题、背包问题、N皇后问题
package com.zuoshen.jichurumen.class08; import java.util.ArrayList; import java.util.Stack; /** * @author ShiZhe * @create 2022-03-12 13:16 */ public class code01 { /** * 汉诺塔问题 * 打印n层汉诺塔从最左边移动到最右边的全部过程 * @param n */ public static void hanoi(int n) { if (n > 0) { func1(n, n, "left", "mid", "right"); } } /** * 汉诺塔问题的递归函数 * 不要去纠结每一个怎么移动,要思考抽象的i怎么移动 * @param i 移动个数 * @param down 当前位置,为了打印输出 * @param from 出发 * @param help 辅助 * @param to 目的 */ public static void func1(int i, int down, String from, String help, String to) { if (i == 1) { System.out.println("Move " + down + " from " + from + " to " + to); } else { // 1————i-1的圆盘从from移到help上 func1(i - 1, down - 1, from, to, help); // 将i圆盘从from移到to上,并打印 func1(1, down, from, help, to); // 将1————i-1的圆盘从help移到to上 func1(i - 1, down - 1, help, from, to); } } /** * 打印一个字符串的全部子序列,包括空字符串 * 输出格式规范化可以添加一个list<char>类型的列表为递归函数的变量 * @param string */ public static void printAllSubsquence(String string) { char[] chars = string.toCharArray(); func2(chars, 0); } /** * 字符串子序列的递归函数 * @param chars * @param i chars数组的i位置 */ public static void func2(char[] chars, int i) { if (i == chars.length) { System.out.println(String.valueOf(chars)); return; } // i位置要 func2(chars, i + 1); char tmp = chars[i]; chars[i] = 0; // i位置不要 func2(chars, i + 1); chars[i] = tmp; } /** * 打印一个字符串的全部排列 * 全排列的时间复杂度大于O(n!)小于O(n*n!) * @param string * @return */ public static ArrayList<String> printAllPermutations(String string) { // 排列结果放入list中 ArrayList<String> result = new ArrayList<>(); if (string == null || string.length() == 0) { return result; } char[] chars = string.toCharArray(); // 递归函数 func3(chars, 0, result); return result; } /** * 全排列的递归函数 * @param chars * @param i * @param result */ public static void func3(char[] chars, int i, ArrayList<String> result) { if (i == chars.length) { result.add(String.valueOf(chars)); } // 加速 boolean[] visit = new boolean[26]; for (int j = i; j < chars.length; j++) { if (!visit[chars[j] - 'a']) { visit[chars[j] - 'a'] = true; swap(chars, i, j); func3(chars, i + 1, result); swap(chars, j, i); } } } public static void swap(char[] chars, int i, int j) { char tmp = chars[i]; chars[i] = chars[j]; chars[j] = tmp; } /** * 纸牌问题 * 递归方法 * @param arr * @return */ public static int cardsInLine(int[] arr) { if (arr == null || arr.length == 0) { return 0; } // 先手和后手 return Math.max(func4(arr, 0, arr.length - 1), func5(arr, 0, arr.length - 1)); } /** * 先手 * @param arr * @param i * @param j * @return */ public static int func4(int[] arr, int i, int j) { if (i == j) { return arr[i]; } return Math.max(arr[i] + func5(arr, i + 1, j), arr[j] + func5(arr, i, j - 1)); } /** * 后手 * @param arr * @param i * @param j * @return */ public static int func5(int[] arr, int i, int j) { if (i == j) { return 0; } return Math.min(func4(arr, i + 1, j), func4(arr, i, j - 1)); } /** * 纸牌问题 * 记忆数组方法 * 二维数组是因为有递归方法中有2个变量,arr数组是不变的 * 先找到数组中初始化可以直接填写的空 * 根据递归看关系,确定填写方向 * 该题是从列从左到右,行从下往上 * @param arr * @return */ public static int cardsInLine2(int[] arr) { if (arr == null || arr.length == 0) { return 0; } int[][] func4 = new int[arr.length][arr.length]; int[][] func5 = new int[arr.length][arr.length]; for (int j = 0; j < arr.length; j++) { func4[j][j] = arr[j]; for (int i = j - 1; i >= 0; i--) { func4[i][j] = Math.max(arr[i] + func5[i + 1][j], arr[j] + func5[i][j -1]); func5[i][j] = Math.min(func4[i + 1][j], func4[i][j -1]); } } return Math.max(func4[0][arr.length - 1], func5[0][arr.length -1]); } /** * 使用递归逆序栈 */ public static void reverseStackUsingRecursive(Stack<Integer> stack) { if (stack.isEmpty()) { return; } int i = getAndRemoveLastElement(stack); reverseStackUsingRecursive(stack); stack.push(i); } /** * 递归获得栈底元素 * @param stack * @return */ 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; } } /** * 数字转化字符串问题 * @param string * @return */ public static int number(String string) { if (string == null || string.length() == 0) { return 0; } return func6(string.toCharArray(), 0); } /** * 数字转化字符串问题递归处理函数 * @param chars * @param i * @return */ public static int func6(char[] chars, int i) { // 到末尾了,说明有一种可能 if (i == chars.length) { return 1; } // 0不能开头,返回 if (chars[i] == '0') { return 0; } // 当前为1,存在后一位就多一种情况 if (chars[i] == '1') { int result = func6(chars, i + 1); if (i + 1 < chars.length) { result += func6(chars, i + 2); } return result; } // 当前为2,后一位存在,同时要求大于等于0并且小于等于6 if (chars[i] == '2') { int result = func6(chars, i + 1); if (i + 1 < chars.length && (chars[i + 1] >= '0' && chars[i + 1] <= '6')) { result += func6(chars, i + 2); } return result; } // 当前大于2 return func6(chars, i + 1); } /** * 背包问题 * 递归方式 * @param weights 重量 * @param values 价值 * @param bag 容量 * @return */ public static int maxValue(int[] weights, int[] values, int bag) { return func7(weights, values, 0, 0, bag); } /** * 背包问题的递归处理函数 * @param weights * @param values * @param i * @param alreadyweight * @param bag * @return */ public static int func7(int[] weights, int[] values, int i, int alreadyweight, int bag) { if (alreadyweight > bag) { return -1; } if (i == weights.length) { return 0; } int p1 = func7(weights, values, i + 1, alreadyweight, bag); int p2next = func7(weights, values, i + 1, alreadyweight + weights[i], bag); int p2 = -1; if (p2next != -1) { p2 = values[i] + p2next; } return Math.max(p1, p2); } /** * 背包问题的非递归处理函数 * 记忆数组 * @param weights * @param values * @param bag * @return */ public static int maxValue2(int[] weights, int[] values, int bag) { int[][] dp = new int[weights.length + 1][bag + 1]; for (int i = weights.length - 1; i >= 0; i--) { for (int j = bag; j >=0; j--) { dp[i][j] = dp[i + 1][j]; if (j + weights[i] <= bag) { dp[i][j] = Math.max(dp[i][j], values[i] + dp[i + 1][j + weights[i]]); } } } return dp[0][0]; } /** * N皇后问题 * 递归方法 * @param n * @return */ public static int nQueens(int n) { if (n < 1) { return 0; } int[] record = new int[n]; return func8(0, record, n); } /** * N皇后问题的递归处理函数 * @param i 行 * @param record 列记录 * @param n * @return */ public static int func8(int i, int[] record, int n) { if (i == n) { return 1; } int result = 0; for (int j = 0; j < n; j++) { if (isValid(record, i, j)) { record[i] = j; result += func8(i + 1, record, n); } } return result; } /** * 判断是否符合条件 * @param record 列记录 * @param i 行 * @param j 列 * @return */ public static boolean isValid(int[] record, int i, int j) { // 遍历某列 for (int k = 0; k < i; k++) { // 列记录有值或同斜线(是否共斜线通过行差与列差相等来判断) if (j == record[k] || Math.abs(record[k] - j) == Math.abs(i - k)) { return false; } } return true; } /** * N皇后问题 * 非递归方法 * 当n <= 32时 * @param n * @return */ public static int nQueens2(int n) { if (n < 1 || n > 32) { return 0; } // 对应皇后可以放的列,1表示可以放,0表示不能放 int limit = n == 32 ? -1 : (1 << n) - 1; return func9(limit, 0, 0, 0); } /** * N皇后问题非递归处理函数 * @param limit 对应皇后可以放的列,1表示可以放,0表示不能放 * @param colLim 列的限制,1不能放,0可以 * @param leftDiaLim 左斜线的限制,1不能放,0可以 * @param rightDiaLim 右斜线的限制,1不能放,0可以 * @return */ public static int func9(int limit, int colLim, int leftDiaLim, int rightDiaLim) { if (colLim == limit) { return 1; } // int pos = 0; // 最右边1的位置 int mostRightOne = 0; // colLim | leftDiaLim | rightDiaLim 表示所有的限制,1表示不能放 // 取反得到,0表示不能放,1表示能放 // 和limit相与,1表示能放,0表示不能放 pos = limit & (~(colLim | leftDiaLim | rightDiaLim)); int res = 0; while (pos != 0) { // 获取最右边的1的位置 mostRightOne = pos & (~pos + 1); // 放最右边的1位置 pos = pos - mostRightOne; // 列限制、左限制、右限制更新 res += func9(limit, colLim | mostRightOne, (leftDiaLim | mostRightOne) << 1, (rightDiaLim | mostRightOne) >>> 1); } return res; } public static void main(String[] args) { // 汉诺塔 int n = 3; hanoi(n); // 字符串子序列 String test = "abc"; printAllSubsquence(test); // 字符创全排列 String string = "abc"; ArrayList<String> strings = printAllPermutations(string); System.out.println(strings); // 纸牌问题 int[] arr = { 1, 9, 1 }; System.out.println(cardsInLine(arr)); System.out.println(cardsInLine2(arr)); // 使用递归逆序栈 Stack<Integer> stack = new Stack<Integer>(); stack.push(1); stack.push(2); stack.push(3); stack.push(4); stack.push(5); reverseStackUsingRecursive(stack); while (!stack.isEmpty()) { System.out.println(stack.pop()); } // 数字转化字符串问题 System.out.println(number("101515610119")); // 背包问题 int[] weights = { 3, 2, 4, 7 }; int[] values = { 5, 6, 3, 19 }; int bag = 11; int i = maxValue(weights, values, bag); System.out.println(maxValue(weights, values, bag)); System.out.println(maxValue2(weights, values, bag)); int n1 = 8; int i1 = nQueens(n1); System.out.println(i1); int i2 = nQueens2(n1); System.out.println(i2); } }