通過分治思想遞迴實現陣列的全排列
阿新 • • 發佈:2018-12-19
在高中數學學習排列與組合的時候,我們知道,要得到一組元素的所有排列情況例如【1,2,3,4,5】這五個數字,它所有的排列一共有5!種,也就是5x4x3x2x1=120種。
這是怎麼得來的呢?我們是將它看作一個長度為5的序列,將每個位置上的可選元素的數目相乘得到的。例如,第一個數可以從1~5中選擇,可選元素是5個,去掉第一位選中的元素,第二位可選元素有4個,以此類推,一共有120種排列方式。
我們在程式設計中借鑑這個過程:陣列第一個數和其他所有的數交換位置,可以得到陣列第一個數可能出現的所有排列情況;然後先忽略掉陣列第一位數字,將剩餘陣列視作一個新的陣列,新陣列的第一個數字再和其他所有數字交換位置,可以得到新陣列第一個數可能出現的所有排列情況,以此類推,將一個數組分治為越來越小的陣列,最後組合出來的排列,就是原陣列的全排列。
/**
* 分治遞迴實現全排列
*
* @author liuxiaofeng
* @date 2018/10/24 13:32
*/
public class PermutationTest {
/**
* 記錄排列數
*/
private int count = 0;
/**
* 直接傳入陣列的過載方法
*
* @param arr 進行排列的陣列
*/
private void permutation(char[] arr) {
permutation(arr, 0, arr.length - 1);
}
/**
* 進行全排列的方法
*
* @param arr 字元陣列
* @param start 陣列開始索引(分治子陣列的起始索引)
* @param end 陣列末尾索引(為減少呼叫length方法次數,將末尾索引值靜態化)
*/
private void permutation(char[] arr, int start, int end) {
if (start == end) {
//排列完成,列印陣列
System. out.println("第 " + (++count) + " 個排列:" + Arrays.toString(arr));
} else {
for (int i = start; i < arr.length; i++) {
//如果當前字元之前未出現過(非重複字元),和其他字元位置進行交換
if (isUnique(arr, start, i)) {
//將當前字元與其他字元交換位置
swap(arr, start, i);
//將當前字元之後的部分視作新的陣列(分治),對其進行排列
permutation(arr, start + 1, end);
//一次迴圈結束後還原字元位置,開始下一次迴圈
swap(arr, start, i);
}
}
}
}
/**
* 字元交換位置的方法
*
* @param arr 字元陣列
* @param index1 索引1
* @param index2 索引2
*/
private void swap(char[] arr, int index1, int index2) {
char tmp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = tmp;
}
/**
* 判斷當前字元是否已經出現過,出現過就是已經交換過,不再進行交換
*
* @param arr 字元陣列
* @param start 當前陣列第一個字元的索引
* @param curr 當前字元索引
* @return 當前字元是否需要進行交換
*/
private boolean isUnique(char[] arr, int start, int curr) {
for (int i = start; i < curr; i++) {
if (arr[curr] == arr[i]) {
//當前字元已經出現過,返回false
return false;
}
}
return true;
}
/**
* 測試方法
*/
@Test
public void test() {
char[] arr = "1268133".toCharArray();
permutation(arr);
}
}