打印數組的全部排列

打印數組的全部排列

作者:Grey

原文地址:

博客園:打印數組的全部排列

CSDN:打印數組的全部排列

無重複值情況

題目描述見: LeetCode 46. Permutations

主要思路

由於是所有排列,所以每個 i 後面位置的元素都有機會來到 i 位置。

定義遞歸函數

void p(int[] arr, int i, List<List<Integer>> result)

遞歸含義是:數組 arr 的 i 位置以後的元素,都來到 i 位置(即:和 i 位置的元素交換),得到的排列是多少。

所以,base case 是,當 i 來到 arr 的最後一個元素的位置的時候,此時可以收集一種排列狀況(因爲最後一個元素後面沒有元素可以與之交換了)

        if (i == arr.length - 1) {
            // 來到最後一個位置,收集答案
            result.add(Arrays.stream(arr).boxed().collect(Collectors.toList()));
            return;
        }

到普遍位置的時候,即遍歷 i 後面的每個位置,讓每個位置都和 i 位置的值交換

        for (int j = i; j < arr.length; j++) {
            swap(arr, i, j);
            p(arr, i + 1, result);
            swap(arr, i, j);
        }

完整代碼見

class Solution {
    public static List<List<Integer>> permute(int[] arr) {
        if (arr == null ) {
            return Collections.emptyList();
        }
        if (arr.length == 0) {
            List<List<Integer>> ans = new ArrayList<>();
            ans.add(new ArrayList<>());
            return ans;
        }
        List<List<Integer>> result = new LinkedList<>();
        p(arr, 0, result);
        return result;
    }

    private static void p(int[] arr, int i, List<List<Integer>> result) {
        if (i == arr.length - 1) {
            // 來到最後一個位置,收集答案
            result.add(Arrays.stream(arr).boxed().collect(Collectors.toList()));
            return;
        }
        for (int j = i; j < arr.length; j++) {
            swap(arr, i, j);
            p(arr, i + 1, result);
            swap(arr, i, j);
        }
    }

    public static void swap(int[] arr, int i, int j) {
        if (i != j) {
            arr[i] = arr[i] ^ arr[j];
            arr[j] = arr[i] ^ arr[j];
            arr[i] = arr[i] ^ arr[j];
        }
    }
}

有重複值情況

題目描述見: LeetCode 47. Permutations II

有重複值的情況可以借鑑上述遞歸方法的思想,我們還是定義如下遞歸函數

void p(int i, int[] arr, List<List<Integer>> result)

遞歸含義還是:數組 arr 的 i 位置以後的元素,都來到 i 位置(即:和 i 位置的元素交換),得到的排列是多少。

但是由於有重複值,所以我們在交換之前,需要判斷和 i 交換的元素曾經有沒有訪問過,如下示意圖

image

當我們將i位置的 a 和 i + n位置的 x 交換過以後,得到了一個全排列的數量,當我們來到i+n+1位置的時候,如果這個位置還是 x ,就無須再和 i 位置的 a 交換了。

爲了標識哪個值是否交換過,我們可以定義一個boolean類型的數組,由於題目中的數據範圍比較小-10<=num[i]<=10, 所以我們只需要一個boolean[] visited = new boolean[21]長度的數組即可。要判斷某個位置的值 m 是否訪問過,只需要判斷visited[m+10]是否爲 TRUE。

完整代碼見

class Solution {
    public static List<List<Integer>> permuteUnique(int[] arr) {
        List<List<Integer>> ans = new ArrayList<>();
        p(0, arr, ans);
        return ans;
    }

    private static void p(int i, int[] arr, List<List<Integer>> result) {
        if (i == arr.length - 1) {
            // 來到最後一個位置,收集答案
            result.add(Arrays.stream(arr).boxed().collect(Collectors.toList()));
            return;
        }
        boolean[] visited = new boolean[21];
        for (int index = i; index < arr.length; index++) {
            if (!visited[arr[index] + 10]) {
                visited[arr[index] + 10] = true;
                swap(index, i, arr);
                p(i + 1, arr, result);
                swap(index, i, arr);
            }
        }
    }

    private static void swap(int i, int j, int[] arr) {
        if (j == i) {
            return;
        }
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }
}

更多

算法和數據結構筆記

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章