全排列演算法及解決數字搭積木問題
如果你是做這道題不會,那麼你可以看這道題的解題思路,如果你是不太理解全排列演算法,那麼你可以通過這個題來理解。
題目描述:
小明最近喜歡搭數字積木。一共有10塊積木,每個積木上有一個數字,0~9。
搭積木規則:
每個積木放到其它兩個積木的上面,並且一定比下面的兩個積木數字小。
最後搭成4層的金字塔形,必須用完所有的積木。
下面是兩種合格的搭法:
請你計算這樣的搭法一共有多少種?
分析
一共有10個數字,要我們把所有可行的排列方式都求出來,一時沒什麼思路,索性就全排列了,把所有排列的情況都求出來,然後在把每種情況都判斷一下,是不是就可以得到答案了。
所以全排列怎麼寫成了第一大問題了。
全排列
對於這個問題來說,我們把金字塔當成一個int陣列,那麼就為 全排列這個陣列{0,1,2,3,4,5,6,7,8,9}。
太長了,想不明白呀,所以來看比較少的唄。
對於{0,1}全排列,就是把0抽出來,1做全排列,對於{0,1,2},就是:
- 把0抽出來,把1,2做全排列,
- 把1抽出來,把0,2做全排列,
- 把2抽出來,把0,1做全排列。
- 接下來那不就和上面那個{0,1}一樣了嗎?
這是不是個遞迴呢?很明顯吧。的確是。
那好我們定義一個方法,這個方法的作用是,把list陣列全排列,而引數curr表示當前抽出來的那個數,就像上面例子提到的0一樣。
提出來了之後呢,是不是要把curr交換了,這樣就可以把所有在這個位置上的所有情況列出來了,所以使用一個for迴圈,來交換curr和後面剩餘陣列的數(就是上面例子的1,2,3步驟)。
緊接著定義一個方法,交換兩數swap(list,curr,j);
重點來了:回溯!!!
從這張圖中,所謂回溯就是要回到上一沒有操作過的狀態,再去考慮別的情況。就下面這個A,B,C他需要回到上一次抽數出來之前的狀態。這樣他才能去抽另外一個數,全排列下一種情況。所以我們需要在寫一遍swap(list,curr,j);
問題分析到這了,我們的程式碼基本上就可以出來了,所以看下程式碼,如果看不懂在回到我的分析,相信你一定能看懂。
public class Test2 { static int sum ; public static void main(String[] args) { int list[] = {0,1,2,3,4,5,6,7,8,9}; allSort(list,0); System.out.println(sum); } //代表將第a[m]和a[n]相交換 public static void swap(int a[],int m ,int n){ int temp = a[m]; a[m] = a[n]; a[n] = temp; } //呼叫全排列陣列list,curr代表當前放在第一個的為第幾個數字,比如開始就為陣列第0個數字 public static void allSort(int list[],int curr ){ //如果當前陣列的索引等於陣列的長了,就將方法加1 if (curr == list.length-1){ check(list); }else { //陣列每一個都要和當前陣列的第curr個相交換,所以要用個迴圈 for (int j = curr; j < list.length; j++){ swap(list,curr,j); allSort(list,curr+1); swap(list,curr,j); } } } public static void check(int list[]){ if (list[1] < list[0]) return; if (list[2] < list[0]) return; if (list[3] < list[1]) return; if (list[4] < list[1]) return; if (list[4] < list[2]) return; if (list[5] < list[2]) return; if (list[6] < list[3]) return; if (list[7] < list[3]) return; if (list[7] < list[4]) return; if (list[8] < list[4]) return; if (list[8] < list[5]) return; if (list[9] < list[5]) return; sum++; } }
其實這個全排列都可以當成一個模板了,但是還是推薦大家一定要手敲,自己寫程式碼,寫出來了,才是自己的東西。