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);
}
}