1. 程式人生 > >演算法-把n個數的每一種排列情況都列出來(排列組合)-全排列-字典序演算法(一看就懂)

演算法-把n個數的每一種排列情況都列出來(排列組合)-全排列-字典序演算法(一看就懂)

首先需要介紹字典序演算法

比如 236541想找到下一個比它大的數

他有3個步驟

1.從最右邊開始找到第一組 左小於右的數 41 54 65 36 23 這樣找,很顯然,我們找到36就找到了,後面的就不用找了。

2.找到之後立刻交換嗎?不是的。定位了這個3以後,再從右邊開始找。找第一個比3大的數。那就是4。這時候我們交換3,4

3.交換以後就算完了嗎?不是的。還要進行一次排序。交換後是這樣的。246531。我們對4(也就是剛才3那個位置)之後進行排序。得出結果為241356

就好了。

原理:其實很簡單。從右邊找過來,就是為了保證下一個數就真的是比這個數大的下一個數。當找到 左小於右這種情況,就是我們要交換的機會。為什麼交換的機會在這裡?因為假如說你要強行找一組 左大於右的並且把他換掉,那豈不是越來越小了? 再換個角度思考。236541,你後面都已經倒序了,你覺得23開頭還有更大的嗎?沒了。

可能又有一個問題。那我6541不倒序,比如236154,你怎麼解釋?那你還是一樣去看待。236以後的154,54又是倒序。所以他第一步的目的,就是略過那些倒序,也就是略過已經實現最大的東西。

接下來幾步相比也不難理解。

程式碼:(分為遞迴和非遞迴兩種方法。雖然是用java寫的,但是不影響其他語言的人閱讀)

//這裡是遞迴形式方法(傳進來的必須是增序的,不然怎麼求全序列?)
private static void Lexi(List<Integer> list) {
    int size = list.size();
//從右往左,找出第一組 左小於右 的數
//現在 1 2 3 4 5
int a = -1; for (int i = size - 1; i >= 1; i --) {//1.找到並記錄第一個左小於右的數 if (list.get(i - 1) < list.get(i)) { a = i - 1; break; } } if (a == -1) {//執行完了退出遞迴 return; } int b = 0; for (int i = size - 1; i > a; i --) {//2.重新開始找,第一個大於我們找到的那個數 if
(list.get(a) < list.get(i)) { b = i; break; } } List<Integer> newList = new ArrayList<>(); newList.addAll(list); Collections.swap(newList, a, b);//進行交換 //排序,a + 1到最後都排一次序 sort(newList, a + 1, size - 1);//最後進行排序 allList.add(newList); Lexi(newList); }

程式碼十分簡單,和我們說的步驟基本一致,但是遞迴在處理大資料的時候回stackoverflow。所以還是用非遞迴形式比較好

//這裡採用的是非遞迴形式,傳進來的必須是增序的
private static void Lexi2(List<Integer> list) {
    List<Integer> mList = new ArrayList<>();
mList.addAll(list);
    int size = mList .size();
//計算需要幾次迴圈:其實就是你將會得到幾個數
int num = 1;
    for (int i = 1; i <= size; i ++) {
        num *= i;
}

    while ((-- num) != 0) {
        int a = 0;
        for (int i = size - 1; i >= 1; i --) {//最後一個數-第二個數
if (mList .get(i - 1) < mList .get(i)) {
                a = i - 1;
                break;
}
        }

        int b = 0;
        for (int i = size - 1; i > a; i --) {
            if (mList .get(i) > mList .get(a)) {
                b = i;
                break;
}
        }

        Collections.swap(mList, a, b);
sort(mList, a + 1, size - 1);
List<Integer> newList = new ArrayList<>();
newList.addAll(mList);
allList.add(newList);
}
}

全部程式碼

public class LexicographicAlgorithm {

    //如果n = 5(即題中的MAX5),那麼應該有60種情況
public static void main(String[] args) throws Exception {

        initData();
Lexi2(list);
//        System.out.println(allList.size() + "");
for (int i = 0; i < allList.size(); i ++) {
            for (int j = 0; j < list.size(); j ++) {
                System.out.print(allList.get(i).get(j) + " ");
}
            System.out.println("");
}
    }

    private static final int MAX = 8;
    private static List<Integer> list = new ArrayList<>();
    private static List<List<Integer>> allList = new ArrayList<>();
    private static void initData() {
        for (int i = 1; i <= MAX; i ++) {
            list.add(i);
}
        allList.add(list);
}

    //這裡是遞迴形式,傳進來的必須是增序的
private static void Lexi(List<Integer> list) {
        int size = list.size();
//從右往左,找出第一組 左小於右 的數
//現在 1 2 3 4 5
int a = -1;
        for (int i = size - 1; i >= 1; i --) {
            if (list.get(i - 1) < list.get(i)) {
                a = i - 1;
                break;
}
        }
        if (a == -1) {//執行完了退出遞迴
return;
}

        int b = 0;
        for (int i = size - 1; i > a; i --) {
            if (list.get(a) < list.get(i)) {
                b = i;
                break;
}
        }
        List<Integer> newList = new ArrayList<>();
newList.addAll(list);
Collections.swap(newList, a, b);
//排序,a + 1到最後都排一次序
sort(newList, a + 1, size - 1);
allList.add(newList);
Lexi(newList);
}

    //這裡採用的是非遞迴形式,傳進來的必須是增序的
private static void Lexi2(List<Integer> list) {
        List<Integer> mList = new ArrayList<>();
mList.addAll(list);
        int size = mList .size();
//計算需要幾次迴圈:其實就是你將會得到幾個數
int num = 1;
        for (int i = 1; i <= size; i ++) {
            num *= i;
}

        while ((-- num) != 0) {
            int a = 0;
            for (int i = size - 1; i >= 1; i --) {//最後一個數-第二個數
if (mList .get(i - 1) < mList .get(i)) {
                    a = i - 1;
                    break;
}
            }

            int b = 0;
            for (int i = size - 1; i > a; i --) {
                if (mList .get(i) > mList .get(a)) {
                    b = i;
                    break;
}
            }

            Collections.swap(mList, a, b);
sort(mList, a + 1, size - 1);
List<Integer> newList = new ArrayList<>();
newList.addAll(mList);
allList.add(newList);
}
    }

    //升序排序
private static void sort(List<Integer> list, int start, int end) {//1 2
if (start == end) {
            return;
} else {
            for (int i = start; i <= end; i ++) {
                int min = Integer.MAX_VALUE;
                int index = -1;
                for (int j = i; j <= end; j ++) {
                    if (list.get(j) < min) {
                        min = list.get(j);
index = j;
}
                }
                Collections.swap(list, i, index);
}
        }
    }
}