如果你對其他算法或者案例感興趣,請考慮閱讀我的以下文章。
遞歸案例-漢諾塔.
遞歸案例-正整數劃分.
動態規劃案例-矩陣連乘(含表格填寫、問題理解、實例解決).
動態規劃案例-最長公共子序列(含表格填寫、問題理解、實例解決、例題答案).
遞歸案例-電路佈線(含表格填寫等超詳細,純人話講解).
問題
設計一個遞歸算法生成n個元素{r1,r2,…,rn}的全排列。
eg:生成3個元素的全排列:{(1,2,3),(1,3,2),(2,1,3),(2,3,1),(3,1,2),(3,2,1)}
問題分析
該類問題的解決方法有兩種:1,固定位置找元素 2,固定元素找位置
本例採用固定位置找元素的方法來解決這個問題。
以數組a[]={1,2,3}爲例,現要生成這三個元素的全排列。
1.我們放入第一個位置上的元素,我們有三個選擇,可以放a[0],a[1],a[2],我們先放入a[0],也就是1
2.我們放入第二個位置上的元素,我們有兩個選擇,可以放a[1]或者a[2]
3.我們放入第三個位置上的元素,只剩下一個選擇。
4-6步的結果爲:
7-9步的結果爲:
算法設計
爲什麼要用遞歸
遞歸的定義:函數調用自身。
我認爲運用遞歸的條件就是:每一步進行的操作基本相同,並且問題規模逐漸減小。
通過上面的例子,首先確定第一個位置上的元素,將問題規模爲n分解爲3個問題規模爲n-1的子問題,其次每一個子問題進行的操作基本相同,都是在找元素。
遞歸函數參數的設計
從上面的問題分析中,我們可以得出3個很重要的元素:數據數組(存儲數據)、位置信息(來確定當前操作的是第幾個位置)、數組數據個數(用來判斷是否排列到最後一個位置)
我們來構造fun(int a[],int I,int n)函數,其中三個參數分別爲:a[]存儲數據的數組、i爲當前位置的序號、n爲數組的個數-1
算法分析
1.首先我們要確定第一個位置上的元素,第一個位置上的元素可以是數組數據中的任何一個,我們可以採用數組元素交換的方式來確定第一個位置上的元素。
2.當我們確定了第一個位置上的元素的時候,我們就需要調用遞歸函數,此時將位置信息+1,來保證我們開始確定第二個位置上的元素。依次下去,直到確定了最後一個元素。
3.在遞歸調用結束後,還需將數組數據元素交換成初始狀態,我們重複第一步的操作,這樣在最後一層遞歸完成之後就會依次將數組數據元素交換回來,使數組恢復成初始狀態,方便我們進行下一個子問題的操作。
for(int j = i;j<=n;j++)
{
swap(a, i ,j);
fun(a, i+1, n);
swap(a, i ,j);
}
4.當遇到遞歸出口的時候,輸出已經排列好的數組中的元素。
if(i==n)
{
for(int j = 0;j<=n;j++)
{
System.out.print(a[j]);
}
System.out.println();
}
代碼演示
遞歸函數
public static void fun(int a[],int i,int n)
{
if(i==n)
{
for(int j = 0;j<=n;j++)
{
System.out.print(a[j]);
}
System.out.println();
}
else
for(int j = i;j<=n;j++)
{
swap(a, i ,j);
fun(a, i+1, n);
swap(a, i ,j);
}
}
交換函數
Java中注意交換,由於Java中沒有指針,所以採用數組引用傳遞。
public static void swap(int a[],int i,int j)
{
int temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
主函數調用
public static void main(String[] args) {
int a[] = new int[]{1,2,3};
fun(a, 0, 2);
}