算法 排列、组合 相关

版权声明:本文为博主原创文章,转载请注明出处 https://github.com/baiiu https://blog.csdn.net/u014099894/article/details/77175485

字符串的去重全排列

字符串全排列

public class Permutation {

    public static void main(String[] args) {
        String s = "abc";
        permutation(s.toCharArray(), 0);
    }

   /*
        从第一个字符起,挨个与后面每个字符交换。
     */
    private static void permutation(char[] chars, int start) {
        if (chars == null || chars.length == 0) return;

        if (start == chars.length - 1) {
            System.out.println(chars);
        } else {
            for (int i = start, length = chars.length; i < length; ++i) {
                if (isSwap(chars, start, i)) {

                    CommonUtil.swap(chars, i, start);
                    permutation(chars, start + 1);
                    CommonUtil.swap(chars, i, start);
                }
            }
        }
    }

    // 与它后面非重复出现的数字交换
    private static boolean isSwap(char[] chars, int start, int end) {
        for (int i = start; i < end; ++i) {
            if (chars[i] == chars[end]) {
                return false;
            }
        }

        return true;
    }
}

八皇后问题

/**
 * 八皇后问题:
 * 在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,
 * 即任意两个皇后不得处在同一行、同一列或者同一对角斜线上。
 * 
 * 求出总共有多少种摆法
 */
public class EightQueue {
    public static void main(String[] args) {
        int[] arr = new int[]{0, 1, 2, 3, 4, 5, 6, 7};

        sortQueue(arr, 0);
    }

    /*
        在[0,8)的数组上,第i个位置上的数字j表示(第i行,第j列)

        0 <= i < 8
        0 <= j < 8

        即j的全排列,每个i上可以放置[0,8)个数字, 8!

        共有8!种方法,排除掉:
            处于同一行的 --> 不可能,在设置的时候就有8个位置了
            处于同一列的 --> 任意两个i上的数字相同,也是不可能的
            任意两个皇后处于同一对角线的 --> 间距相等了
     */
    private static void sortQueue(int[] arr, int start) {
        if (start == arr.length - 1) {
            // 过滤
            if (isDiagonal(arr)) { 
               // 是对角线的不输出
                return;
            }

            CommonUtil.printArray(arr);

        } else {
            for (int i = start, length = arr.length; i < length; ++i) {
                CommonUtil.swap(arr, start, i);
                sortQueue(arr, start + 1);
                CommonUtil.swap(arr, start, i);
            }
        }
    }

    private static boolean isDiagonal(int[] arr) {

        for (int i = 0, length = arr.length; i < length; ++i) {
            for (int j = i + 1; j < length; ++j) {

                if (arr[i] - arr[j] == i - j || arr[i] - arr[j] == j - i) {
                    return true;
                }

            }
        }

        return false;
    }
}

字符串的组合

/**
 * <p>
 * 输入一个字符串,输出该字符串中字符的所有组合。
 * 举个例子,如果输入abc,它的组合有a、b、c、ab、ac、bc、abc。
 */
public class Combination {

    /*
        2^n -1 个
     */
    public static void main(String[] args) {
        String s = "abc";

        combination_bit(s.toCharArray()); // 位运算
        combination(s.toCharArray()); // 递归
    }

  /*
        共有 2^n -1 中组合

        使用位运算, 001表示a, 010表示b, 100表示c
        遍历 [1, 2^n) 个数字,对每个数字进行位运算,找出所有组合

        O(2^n) 指数级
     */
    private static void combination_bit(char[] chars) {
        if (chars == null || chars.length == 0) return;

        int length = chars.length;
        int n = 1 << length;

        StringBuilder builder = new StringBuilder();
        for (int i = 1; i < n; ++i) {
            for (int j = 0; j < length; ++j) {
                if ((i & (1 << j)) != 0) { // 001 010 100
                    builder.append(chars[j]);
                }
            }
            System.out.println(builder.toString());

            builder.delete(0, builder.length());
        }
    }

    /*
        在长度为n的字符串中求m个字符的组合, 1 <= m <= n。我们先从头扫描字符串的第一个字符。

        针对第一个字符,我们有两种选择:
            第一是把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选取m-1个字符;
            第二是不把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选择m个字符。
            这两种选择都很容易用递归实现。
     */
    private static void combination(char[] chars) {
        if (chars == null || chars.length == 0) return;

        Stack<Character> stack = new Stack<>();
        for (int i = 1, length = chars.length; i <= length; ++i) {
            combination(chars, 0, i, stack);
        }
    }

    /*
        start指扫描到哪个字符了
        number是指几位组合, 1位组合还是m位组合
     */
    private static void combination(char[] chars, int start, int number, Stack<Character> stack) {
        if (number == 0) {
            System.out.println(stack.toString());
            return;
        }

        if (start == chars.length) {
            return;
        }

        stack.push(chars[start]);
        combination(chars, start + 1, number - 1, stack);
        stack.pop();
        combination(chars, start + 1, number, stack);
    }
}

组合习题一

/**
 * 输入两个整数n和m,
 * 从数列1,2,3...n中随意取几个数,使其和等于m,要求列出所有的组合。
 */
public class EqualsMCombination {

    public static void main(String[] args) {
        // 找出所有组合,输出和为m的组合
        findSumIsM(5, 12);
        System.out.println();
        System.out.println();

        // 以和为target寻找
        findSumIsM_Simple(5, 12);
    }

    /*
        从最大值开始开始寻找,即从n开始

        对于每个n,都有两种选择:
            1. 将即计入和m中,在剩下的n-1个数字中继续寻找和为 n-m 的数字
            2. 不计算于其中,在剩下的 n-1 个数字中寻找和为m 的数字

        终止条件: m==0(找完了)
                 n<0 || m<0 (找不到)
     */
    private static void findSumIsM_Simple(int n, int m) {
        if (m > ((1 + n) * n / 2)) {
            System.out.println("m太大了");
        }

        Stack<Integer> stack = new Stack<>();
        findIt_Simple(n, m, stack);
    }

    private static void findIt_Simple(int n, int m, Stack<Integer> stack) {
        if (n < 0 || m < 0) {
            return;
        }

        if (m == 0) {
            System.out.println(stack);
            return;
        }


        stack.push(n);
        findIt_Simple(n - 1, m - n, stack);
        stack.pop();
        findIt_Simple(n - 1, m, stack);

    }


    /*
        [1,n] 中,获取所有的组合,输出和为m的组合

        分两步:
            1. 获取所有组合 (就可以使用之前字符串组合的方式)
            2. 输出所有和为m的组合

        在n个数字中选取m个数字(1<=m<=n), 从1开始遍历数列,针对第一个数字:
           将这个数字放到组合中,在剩下的 n-1个 数字中选取m - 1个
           不把这个数字放到组合中,在剩下的 n-1 个 数字中选取m个
     */
    private static void findSumIsM(int n, int m) {
        if (m > ((1 + n) * n / 2)) {
            System.out.println("没有满足的序列,超过所有数字之和");
        }

        Stack<Integer> stack = new Stack<>();
        for (int i = 1; i <= n; ++i) {
            findIt(n, 1, i, stack, m);
        }
    }

    private static void findIt(int n, int start, int number, Stack<Integer> stack, int targetSum) {
        if (number == 0) {
            int sum = 0;
            for (Integer integer : stack) {
                sum += integer;
            }

            if (sum == targetSum) {
                System.out.println(stack);
            }

            return;
        }

        if (start == n + 1) {
            return;
        }

        stack.push(start);
        findIt(n, start + 1, number - 1, stack, targetSum);
        stack.pop();
        findIt(n, start + 1, number, stack, targetSum);
    }

}

组合习题二

就是一个特殊情况

/**
 * Given two integers n and k,
 * return all possible combinations of k numbers out of 1 ... n.
 * <p>
 * 从[1...n]中选取k个数的所有组合
 */
public class LimitedCombination {

    public static void main(String[] args) {
        int n = 5;
        int k = 2;
        Stack<Integer> stack = new Stack<>();

        combination(n, 0, k, stack);
    }


    /*
        从n个数中选取k个数,从第一个数开始
            1) 选择将其放入组合内,从剩下的 n-1 个数中选取 k-1 个数
            2) 不将其放入组合内,从剩下的 n-1 个数中 选取 k 个数
     */
    private static void combination(int n, int start, int number, Stack<Integer> stack) {
        if (number == 0) {
            System.out.println(stack);
            return;
        }

        if (start == n + 1) {
            return;
        }

        stack.push(start);
        combination(n, start + 1, number - 1, stack);
        stack.pop();
        combination(n, start + 1, number, stack);
    }

}




参考:
字符串的全排列和组合算法
July 算法习题 - 字符串4(全排列和全组合)

猜你喜欢

转载自blog.csdn.net/u014099894/article/details/77175485