原本預算此章節是繼續寫快速排序的,然而編寫快速排序每每是遞歸來寫的,而且遞歸可能不是那麼好理解,因而就有了這篇文章。java
在上面提到了遞歸這麼一個詞,遞歸在程序語言中簡單的理解是:方法本身調用本身算法
遞歸其實和循環是很是像的,循環都能夠改寫成遞歸,遞歸未必能改寫成循環,這是一個充分沒必要要的條件。編程
咱們初學編程的時候確定會作過相似的練習:數組
1+2+3+4+....+100(n)
求和咱們要記住的是,想要用遞歸必須知道兩個條件:bash
技巧:在遞歸中經常是將問題切割成兩個部分(1和總體的思想),這可以讓咱們快速找到遞歸表達式(規律)微信
若是咱們使用for
循環來進行求和1+2+3+4+....+100
,那是很簡單的:編程語言
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum = sum + i;
}
System.out.println("公衆號:Java3y:" + sum);
複製代碼
前面我說了,for循環均可以使用遞歸來進行改寫,而使用遞歸必需要知道兩個條件:一、遞歸出口,二、遞歸表達式(規律)測試
首先,咱們來找出它的規律:1+2+3+...+n
,這是一個求和的運算,那麼咱們能夠假設X=1+2+3+...+n
,能夠將1+2+3+...+(n-1)
當作是一個總體。而這個總體作的事又和咱們的**初始目的(求和)**相同。以咱們的高中數學知識咱們又能夠將上面的式子當作X=sum(n-1)+n
spa
好了,咱們找到咱們的遞歸表達式(規律),它就是sum(n-1)+n
,那遞歸出口呢,這個題目的遞歸出口就有不少了,我列舉一下:3d
n=1
時,那麼就返回1
n=2
時,那麼就返回3
(1+2)n=3
時,那麼就返回6
(1+2+3)固然了,我確定是使用一個最簡單的遞歸出口了:if(n=1) return 1
遞歸表達式和遞歸出口咱們都找到了,下面就代碼演示:
遞歸出口爲1:
public static void main(String[] args) {
System.out.println("公衆號:Java3y:" + sum(100));
}
/** * * @param n 要加到的數字,好比題目的100 * @return */
public static int sum(int n) {
if (n == 1) {
return 1;
} else {
return sum(n - 1) + n;
}
}
複製代碼
遞歸出口爲4:
public static void main(String[] args) {
System.out.println("公衆號:Java3y:" + sum(100));
}
/** * * @param n 要加到的數字,好比題目的100 * @return */
public static int sum(int n) {
//若是遞歸出口爲4,(1+2+3+4)
if (n == 4) {
return 10;
} else {
return sum(n - 1) + n;
}
}
複製代碼
結果都是同樣的。
若是使用的是循環,那麼咱們一般這樣實現:
int[] arrays = {2, 3, 4, 5, 1, 5, 2, 9, 5, 6, 8, 3, 2};
//將數組的第一個假設是最大值
int max = arrays[0];
for (int i = 1; i < arrays.length; i++) {
if (arrays[i] > max) {
max = arrays[i];
}
}
System.out.println("公衆號:Java3y:" + max);
複製代碼
那若是咱們用遞歸的話,那怎麼用弄呢?首先仍是先要找到遞歸表達式(規律)和遞歸出口
2
與數組後面的數->{3, 4, 5, 1, 5, 2, 9, 5, 6, 8, 3, 2}
進行切割,將數組後面的數當作是一個總體X={3, 4, 5, 1, 5, 2, 9, 5, 6, 8, 3, 2}
,那麼咱們就能夠當作是第一個數和一個總體進行比較if(2>X) return 2 else(2<X) return X
2
進行比較。找出總體的最大值又是和咱們的**初始目的(找出最大值)**是同樣的if( 2>findMax() )return 2 else return findMax()
使用到數組的時候,咱們一般爲數組設定左邊界和右邊界,這樣比較好地進行切割
arrays.length-1
(長度-1在角標中才是表明最後一個元素)那麼能夠看看咱們遞歸的寫法了:
public static void main(String[] args) {
int[] arrays = {2, 3, 4, 5, 1, 5, 2, 9, 5, 6, 8, 3, 1};
System.out.println("公衆號:Java3y:" + findMax(arrays, 0, arrays.length - 1));
}
/** * 遞歸,找出數組最大的值 * @param arrays 數組 * @param L 左邊界,第一個數 * @param R 右邊界,數組的長度 * @return */
public static int findMax(int[] arrays, int L, int R) {
//若是該數組只有一個數,那麼最大的就是該數組第一個值了
if (L == R) {
return arrays[L];
} else {
int a = arrays[L];
int b = findMax(arrays, L + 1, R);//找出總體的最大值
if (a > b) {
return a;
} else {
return b;
}
}
}
複製代碼
在冒泡排序章節中給出了C語言的遞歸實現冒泡排序,那麼如今咱們已經使用遞歸的基本思路了,咱們使用Java來重寫一下看看:
冒泡排序:倆倆交換,在第一趟排序中可以將最大值排到最後面,外層循環控制排序趟數,內層循環控制比較次數
以遞歸的思想來進行改造:
L==R
public static void main(String[] args) {
int[] arrays = {2, 3, 4, 5, 1, 5, 2, 9, 5, 6, 8, 3, 1};
bubbleSort(arrays, 0, arrays.length - 1);
System.out.println("公衆號:Java3y:" + arrays);
}
public static void bubbleSort(int[] arrays, int L, int R) {
int temp;
//若是隻有一個元素了,那什麼都不用幹
if (L == R) ;
else {
for (int i = L; i < R; i++) {
if (arrays[i] > arrays[i + 1]) {
temp = arrays[i];
arrays[i] = arrays[i + 1];
arrays[i + 1] = temp;
}
}
//第一趟排序後已經將最大值放到數組最後面了
//接下來是排序"總體"的數據了
bubbleSort(arrays, L, R - 1);
}
}
複製代碼
接觸過C語言的同窗極可能就知道什麼是費波納切數列了,由於每每作練習題的時候它就會出現,它也是遞歸的典型應用。
菲波那切數列長這個樣子:{1 1 2 3 5 8 13 21 34 55..... n }
數學好的同窗可能很容易就找到規律了:前兩項之和等於第三項
例如:
1 + 1 = 2
2 + 3 = 5
13 + 21 = 34
複製代碼
若是讓咱們求出第n項是多少,那麼咱們就能夠很簡單寫出對應的遞歸表達式了:Z = (n-2) + (n-1)
遞歸出口在本題目是須要有兩個的,由於它是前兩項加起來才得出第三項的值
一樣地,那麼咱們的遞歸出口能夠寫成這樣:if(n==1) retrun 1 if(n==2) return 2
下面就來看一下完整的代碼吧:
public static void main(String[] args) {
int[] arrays = {1, 1, 2, 3, 5, 8, 13, 21};
//bubbleSort(arrays, 0, arrays.length - 1);
int fibonacci = fibonacci(10);
System.out.println("公衆號:Java3y:" + fibonacci);
}
public static int fibonacci(int n) {
if (n == 1) {
return 1;
} else if (n == 2) {
return 1;
} else {
return (fibonacci(n - 1) + fibonacci(n - 2));
}
}
複製代碼
圖片來源百度百科:
玩漢諾塔的規則很簡單:
咱們下面就來玩一下:
從前三次玩法中咱們就能夠發現的規律:
簡單來講就分紅三步:
那麼就能夠寫代碼測試一下了(回看上面玩的過程):
public static void main(String[] args) {
int[] arrays = {1, 1, 2, 3, 5, 8, 13, 21};
//bubbleSort(arrays, 0, arrays.length - 1);
//int fibonacci = fibonacci(10);
hanoi(3, 'A', 'B', 'C');
System.out.println("公衆號:Java3y" );
}
/**
* 漢諾塔
* @param n n個盤子
* @param start 起始柱子
* @param transfer 中轉柱子
* @param target 目標柱子
*/
public static void hanoi(int n, char start, char transfer, char target) {
//只有一個盤子,直接搬到目標柱子
if (n == 1) {
System.out.println(start + "---->" + target);
} else {
//起始柱子藉助目標柱子將盤子都移動到中轉柱子中(除了最大的盤子)
hanoi(n - 1, start, target, transfer);
System.out.println(start + "---->" + target);
//中轉柱子藉助起始柱子將盤子都移動到目標柱子中
hanoi(n - 1, transfer, start, target);
}
}
複製代碼
咱們來測試一下看寫得對不對:
參考資料:
遞歸的確是一個比較難理解的東西,好幾回都把我繞進去了....
要使用遞歸首先要知道兩件事:
在遞歸中經常用」總體「的思想,在漢諾塔例子中也不例外:將最大盤的盤子當作1,上面的盤子當作一個總體。那麼咱們在演算的時候就很清晰了:將」總體「搬到B柱子,將最大的盤子搬到C柱子,最後將B柱子的盤子搬到C柱子中
由於咱們人腦沒法演算那麼多的步驟,遞歸是用計算機來乾的,只要咱們找到了遞歸表達式和遞歸出口就要相信計算機能幫咱們搞掂。
在編程語言中,遞歸的本質是方法本身調用本身,只是參數不同罷了。
最後,咱們來看一下若是是5個盤子,要運多少次才能運完:
PS:若是有更好的理解方法,或者我理解錯的地方你們能夠在評論下留言一塊兒交流哦!
若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y