0
点赞
收藏
分享

微信扫一扫

java全排列实现

boomwu 2022-02-26 阅读 89

/**
 * 全排列
 */
public class PermutationTest {

    static List<String> list = new ArrayList<>();

    public static void main(String[] args) {
        String s = "abcd";
        //new PermutationTest().PermutationV1(s.toCharArray(), 0);
        //new PermutationTest().PermutationV2(s.toCharArray(), 0, s.length() - 1);
        List<List<Character>> lists = new PermutationTest().PermutationV3(s.toCharArray());
        for (List<Character> chars : lists) {
            System.out.println(chars);
        }
    }

    public void PermutationV1(char chs[], int start) {
        if (start == chs.length) {
            list.add(new String(chs));
        }
        for (int i = start; i <= chs.length - 1; i++) {
            if (i == start || chs[i] != chs[start]) {
                //在排列的时候进行判断如果后面的元素与start相同时就不进行排序。
                //这样就可以避免对重复元素进行排序
                Swap(chs, i, start);
                PermutationV1(chs, start + 1);
                Swap(chs, i, start);
            }
        }
    }

    private void PermutationV2(char[] chars, int begin, int end) {

        if (begin == end) {
            list.add(new String(chars));
            return;
        }

        for (int i = begin; i <= end; i++) {
            if (!IsSwap(chars, begin, i)) {
                continue;
            }
            Swap(chars, begin, i);
            //begin+1前面的不用管、因为已经由前面的递归进行过交换了、每次把下一个子串丢尽递归
            //例如abcd、bcd、cd
            PermutationV2(chars, begin + 1, end);
            Swap(chars, begin, i);
        }
    }

    public List<List<Character>> PermutationV3(char[] chars) {
        int len = chars.length;
        // 使用一个动态数组保存所有可能的全排列
        List<List<Character>> res = new ArrayList<>();
        if (len == 0) {
            return res;
        }
        boolean[] used = new boolean[len];
        List<Character> path = new ArrayList<>();

        dfs(chars, len, 0, path, used, res);
        return res;
    }

    private boolean IsSwap(char[] chars, int begin, int end) {
        for (int j = begin; j < end; j++) {
            /**
             * a[end] 是等待被交换的元素
             * 如果 在 [begin, end) 范围里存在和 a[end] 相同的元素则不进行交换,说明这种情况已经存在了。
             * 比如当 begin 为 0 时,{3, 3, 2, 3, 4} 这个序列在第一个位置只有三种情况:3、2、4,如果等待被交换的元素 a[end] 在之前出现过,说明这种情况已经存在了
             * begin坐标一直++   假如begin坐标下的元素与end所在坐标元素相同  则说明end前面已经有相同的元素跟begin交换过、现在这个相同的元素就没必要再交换了
             */
            if (chars[j] == chars[end]) {
                return false;
            }
        }
        return true;
    }


    private void dfs(char[] chars, int len, int depth,
                     List<Character> path, boolean[] used,
                     List<List<Character>> res) {
        if (depth == len) {
            res.add(new ArrayList<Character>(path));
            return;
        }

        // 在非叶子结点处,产生不同的分支,这一操作的语义是:在还未选择的数中依次选择一个元素作为下一个位置的元素,这显然得通过一个循环实现。
        for (int i = 0; i < len; i++) {
            if (!used[i]) {
                path.add(chars[i]);
                used[i] = true;

                dfs(chars, len, depth + 1, path, used, res);
                // 注意:下面这两行代码发生 「回溯」,回溯发生在从 深层结点 回到 浅层结点 的过程,代码在形式上和递归之前是对称的
                used[i] = false;
                path.remove(path.size() - 1);
            }
        }
    }

    public void Swap(char chs[], int i, int j) {
        char temp;
        temp = chs[i];
        chs[i] = chs[j];
        chs[j] = temp;
    }
}

举报

相关推荐

0 条评论