遞歸案例-全排列

如果你對其他算法或者案例感興趣,請考慮閱讀我的以下文章。

遞歸案例-漢諾塔.
遞歸案例-正整數劃分.
動態規劃案例-矩陣連乘(含表格填寫、問題理解、實例解決).
動態規劃案例-最長公共子序列(含表格填寫、問題理解、實例解決、例題答案).
遞歸案例-電路佈線(含表格填寫等超詳細,純人話講解).

問題

設計一個遞歸算法生成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

1
null
位置1
位置2
位置3
null
null
null

2.我們放入第二個位置上的元素,我們有兩個選擇,可以放a[1]或者a[2]

1
2
位置1
位置2
位置3
3
null
null

3.我們放入第三個位置上的元素,只剩下一個選擇。

1
2
位置1
位置2
位置3
3
3
2

4-6步的結果爲:

2
1
位置1
位置2
位置3
3
3
1

7-9步的結果爲:

3
1
位置1
位置2
位置3
2
2
1

算法設計

爲什麼要用遞歸

遞歸的定義:函數調用自身。
我認爲運用遞歸的條件就是:每一步進行的操作基本相同,並且問題規模逐漸減小。
通過上面的例子,首先確定第一個位置上的元素,將問題規模爲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);
 }

輸出結果

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章