最淺顯易懂的全排列演算法Java實現
技術標籤:演算法與資料結構
對於全排列這個教科書上的入門級演算法,當初我自己可是又愛又恨,是它讓我體會到了時間倒流一樣的恐怖,又讓我油然而生一種解決問題的自豪。在這裡,我將嘗試拋開繁文縟節,從任何人都能理解的案例和思路出發,一步步踏入全排列的深坑。
(1)全排列是什麼?
其實就是針對一個數組,輸出它裡面元素可能組成的全部不重複的組合。例如:
{1,2,3} 經過全排列後得到:
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 2, 1]
[3, 1, 2]
(2)具體步驟羅列:
不著急敲程式碼實現,我們第一步都是先看看它的步驟,再看看操作有沒有共性的地方,能不能抽出來做遞迴呼叫的,所以第一步,不嫌麻煩地羅列每一步操作:
按照正常思維,我們都是先把頭部單獨出來,這樣原來陣列砍掉了頭部,可以看作是新的一個數組,這個新的陣列又可以砍掉頭部,又繼續生成新陣列,直到最後一個元素,其次,陣列元素直接肯定還要互換位置,輪流做頭部,輪流做尾部,才能充分產生每一種組合情況。
例:
陣列1,2,3
第一輪外迴圈:1與1交換位置(不變),首先抓住1 當頭。剩下2,3進入遞迴
第一輪內迴圈
2與2交換位置(不變),抓住 2 當頭,剩下3
輸出1,2,3
第二輪內迴圈
2與3交換位置,抓住3當頭,剩下2
輸出1,3,2
第二輪外迴圈:1與2交換位置,抓住2 當頭。剩下1 ,3進入遞迴
第一輪內迴圈
1與1交換位置(不變),抓住 1 當頭,剩下3
輸出2,1,3
第二輪內迴圈
1與3交換位置,抓住3當頭,剩下2
輸出2,3,1
第三輪外迴圈:1與3交換位置,抓住3 當頭。剩下1 ,2進入遞迴
第一輪內迴圈
1與1交換位置(不變),抓住 1 當頭,剩下 2
輸出 3, 1,2
第二輪內迴圈
1與2交換位置,抓住2當頭,剩下1
輸出3,2,1
根據這個詳細步驟,我們可以看到需要注意幾點:
(1)每個元素都肯定有機會輪流當老大(頭部第一位)
(2)換位置後記得換回來
(3)內迴圈和外迴圈似乎是一樣的邏輯,那就可以利用遞迴迴圈呼叫了
(3)具體步驟清晰了,嘗試著寫虛擬碼
Array = {1,2,3 };
end = Array.legth - 1;
Full( Array , 0 , end)
Function Full (Array , begin , end ){
if( begin == end ) {
// 觸底結束,做輸出
}
for( i = begin ; i <=end ; i++ ){
swap ( Array , begin , i );
Full( Array , begin+1 , end );
swap ( Array , begin , i );
}
}
(4)實際程式碼:
public static void main(String[] args) {
int[] array = new int[]{1,2,3};
int length = array.length;
fullSort(array, 0, length-1);
}
private static void fullSort(int[] array, int begin, int end){
// 每次遞迴迴圈的開始都咬定一個頭
if (begin == end) {
// 到底了就輸出
System.out.println(Arrays.toString(array));
} else {
for (int i = begin; i <= end; i++) {
// 這一步交換保證了大家輪流做一遍老大
swap(array, begin, i);
// 遞迴的目的是讓除當前老大後剩下的元素列表繼續選舉老大
fullSort(array, begin + 1, end);
// 復位是當完老大後按照原來位置坐回去,方便重新開始選舉老大
swap(array, begin, i);
}
}
}
private static void swap(int[] array, int begin, int end){
int temp = array[begin];
array[begin] = array[end];
array[end] = temp;
}
結果:
(5)難點解析(個人覺得比較難理解的點)
(1)begin那個點是牢牢抓住陣列第一位下標的,而for迴圈自增的i ,就是不斷拿各個元素去跟第一位下標的元素換位置,也就是迴圈當老大。所以begin的確立很重要。
(2)遞迴中begin是當前迴圈的begin + 1 ;其實就是確立砍掉頭後的新陣列的新老大的下標位置。
(3)一定要記得遞迴for到最後一個元素完成後,復位,一步一步把調換的位置又回覆過來,不然繼續迴圈就會出現有的人做了多次老大,有的人一次都沒做過老大。
(6)生活案例
有三個小朋友(小明,小紅,小花)玩嘟嘟嘟開火車小遊戲
小明先做火車頭,小紅做二號車廂,小花做三號車廂
跑完一圈,小花對小紅說:我不想做尾巴,我想試試中間的位置,於是小花小紅換位置。
小明做火車頭,小花做二號車廂,小紅做三號車廂又跑了一圈
跑完後小花說:我體驗過了,我們換回來吧,於是小紅和小花換回了位置
小明說:火車頭太累了,下一個人當,誰第二節車廂來著?
於是小紅當上了火車頭,小明當了第二節車廂,小花還是第三
跟上面一樣,嘟嘟嘟跑完一圈,小明跟小花換個位置體驗一下互相拉衣服的感覺,又跑一圈,才換回來
小紅跑了兩圈又累了,於是跟小明說:行了我們換回來吧,好累啊
小明換回來,說:我也跑了兩圈,小花沒當過車頭,不公平
於是小明與小花換了位置,小明當了三號車廂,小花當了火車頭,嘟嘟嘟跑了一圈
第二圈,小明跟小紅換了位置,嘟嘟嘟又一圈。跑完之後又換回位置。
小花也跑完了,說:小明我們換回來吧
於是 跟開始的火車順序一樣:小明,小紅,小花。
每個人在每個位置都體驗過了,大家都公平了,於是心滿意足結束了遊戲