算法基础入门——汉诺塔问题、字符串的子序列、字符串的全排列、纸牌赢家问题、递归逆序栈、数字转化字符串问题、背包问题、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);
    }
}

 

posted @ 2022-03-15 12:36  北漂的尘埃  阅读(41)  评论(0编辑  收藏  举报