遞歸

1.遞歸是什么?

定義:程序調用自身的編程技巧稱為遞歸。
遞歸使用的是選擇結構,對于解決同樣問題的孿生兄弟:迭代,它使用的則是循環結構。

2.遞歸的核心

先看一個例子,表達式1+2+3....+100=?要怎么寫程序來計算?
第一反應是for循環:

int sum =0;
for (int i = 1; i <= 100; i++) {
sum+=i;
}
System.out.println(sum);//5050

更簡潔的寫法

int start =1;
int end = 100;
int sum =(start+end)*end/2;//首項加末項乘以項數除以二
System.out.println(sum);//5050

遞歸的寫法

static int recursion(int n){
if(n==1){//遞歸出口
return 1;
}else{
return n+recursion(n-1);
}
}

遞歸的核心其實就是一個if...else... 一個條件跳出,一個條件繼續。

1.優點:使程序結構更清晰,更簡潔,更容易讓人理解,
2.缺點:使用遞歸調用時,**如果過多的調用容易造成java.lang.StackOverflowError即棧溢出和程序執行過慢。這是一個潛在Bug和影響程序執行效率問題,需要謹慎使用。**對于互聯網這種以速度和效率來維護用戶量,不得以用遞歸時,可以把處理的數據放入緩存,或者直接使用迭代等方式來解決。
3.規律:遞歸要有出口,不然成了死循環。解出遞歸的要點在于求出n-1,求出了n-1才能求解出n,這是為什么呢?

3.例題

3.1.題目:有一對兔子,從出生后第3個月起每個月都生一對兔子, 小兔子長到第四個月后每個月又生一對兔子, 假如兔子都不死,問每個月的兔子總數為多少?

分析,這個題目是著名的斐波那契數列:1,1,2,3,5,8,13,21....=Sn = Sn-1+Sn-2
規律是:從第三個數開始,每個數都是前兩個數的和。

迭代方式

int month =10;
int sum[]= new int[month] ; //初始化月份數組
sum[0] = 1; //第一個月
sum[1] = 1; //第二個月
for(int i=2;i<=month-1;i++){
sum[i] = sum[i-1]+sum[i-2]; //第三個月等于前兩個月之和
}
System.out.println("第"+month+"個月的兔子總數是:"+sum[month-1]);

遞歸方式

static int recursion(int i){
if( i == 1 || i == 2 ){
return 1;
}
else{
return recursion(i-1) + recursion(i-2);//第三項等于后兩項之和
}
}

3.2 題目: 逆序排列字符單詞,輸入:I love java-->java love I

迭代方式

static String reverse(String str){
String[] strs = str.split(" ");
StringBuilder sb = new StringBuilder();
//倒敘遍歷并拼接空格
for (int i = strs.length-1; i >= 0; i--) {
sb.append(strs[i]+" ");
}
return sb.toString();
}

遞歸方式

static String reverse(String s) {
int i = s.indexOf(" ");//搜索第一次出現" "的位置
if (s == null||i == -1) {
return s;
}
return reverse(s.substring(i + 1)) + " " + s.substring(0, i);//每次截取第一個單詞放在最后拼接
}

3.3.題目:使用遞歸來統計字符串String str="hello"的長度,不能使用統計變量(只能用遞歸求解).

static int test1(String str){
if(null==str||str.equals("")){
return 0;
}
String[] strs = str.split("");
StringBuffer sb = new StringBuffer();//每次截取最后一個字母
for(int i = 0; i <= strs.length-2; i++)
{
sb. append(strs[i]);
}
return test1(sb.toString())+1;//每次遞歸調用+1
}

3.4 題目:實現二分查找算法.

二分查找,不斷將數組進行對半分割,每次拿中間元素和goal進行比較(前提是數組元素的排序應該是遞增或者遞減)

public static void main(String[] args) {
int [] arr={1,2,5,6,8,9,12,64,78,90};//有序數組
System.out.println(test(arr,0,arr.length-1,8));//傳入數組和數組長度,最后一個是要查找的值
}
//遞歸方式實現
public static int recursion(int [] arr,int low,int high,int value){
if(low>high){
return -1;
}
int mid=(low+high)/2;//求中間的值
if(value==arr[mid]){//如果相等,則找到該值,直接返回
return mid;
}else if(value<arr[mid]){//如果要找的值在中間值得左邊,則下一次遞歸開始的右指針指向該次中間值-1
return recursion(arr,low,mid-1,value);
}else{////如果要找的值在中間值得右邊,則下一次遞歸開始的左指針指向該次中間值+1
return recursion(arr,mid+1,high,value);
}
}
//循環方式實現
public static int test(int [] arr,int low,int high,int value){
int mid;
while(high>=low){
mid=(low+high)/2;
if(value<arr[mid]){
high=mid-1;
}else if(value>arr[mid]){
low=mid+1;
}else{
return mid;
}
}
return -1;//都沒找到,則返回-1
}

3.5題目:求最大公約數和最小公倍數:

public static void main(String[] args) {
int x = 100;
int y = 18;
System.out.println("最大公約數:"+gcd(x,y));
System.out.println("最小公倍數:"+lcm(x,y));
}
//輾轉相除法實現最大公約數
public static int gcd(int x, int y) {
if (y == 0){
return x;
}else{
return gcd(y, x % y);//x%y時,如果x<y則返回x,例:4%20=4 5%20=5,迭代之后會把小數放在后面,所以不用做交換
}
}
//最小公倍數 
public static int lcm(int p,int q){
int pq = p * q;
return pq / gcd(p,q);
}

3.6.題目:楊輝三角

        1 
       1 1 
      1 2 1 
     1 3 3 1 
    1 4 6 4 1 
   1 5 10 10 5 1 
 1 6 15 20 15 6 1 

分析:楊輝三角最本質的特征是,它的兩條斜邊都是由數字1組成的,而其余的數則是等于它肩上的兩個數之和。

public static void main(String[] args) {
int n = 7;
for (int i = 1; i <= n; i++) {
//雙層for循環是為了打印出三角形
for (int j = 1; j <= n-i; j++) {//每行前面的空格數
System.out.print(" ");
}
for (int j = 1; j <= i; j++) {
System.out.print(num(i,j)+" ");
}
System.out.println();//換行
}
}
public static int num(int x,int y){
if(y==1||y==x){
return 1;
}
return num(x-1,y-1)+num(x-1,y);//每一個數等于肩上兩個數之和
}

3.7.題目:漢諾塔

接下來就到了遞歸的經典案例漢諾塔問題,本文就不對漢諾塔游戲規則進行講解,如果以前沒接觸過漢諾塔,建議先玩玩漢諾塔游戲,總結一下游戲規律。

現在要把X柱上所有圓盤移動到Z
當移動3個圓盤


當移動6個圓盤

所以可以推出,當n個從x柱,經由y柱中轉,移動到z柱(解出n層漢諾塔時),有:
當n=0時,
不用做任何操作
當n>0時,
首先,將n-1個盤子從x借助z移動到y
然后,將1個盤子從x移動到z
最后,將在中間y上的n-1個盤子借助x移動到z
為了解出n層漢諾塔,需要先使用n-1層漢諾塔的解法。

static int t=0;//最少移動次數
public static void main(String[] args) {
  hanio(3,"x","y","z");
  System.out.println(t);
}
static void hanio(int n ,String src,String mid,String dest){
  if(n==1){
    System.out.println(src+"-->"+dest);//移動過程
    t++;
  }else{
    hanio(n-1,src,dest,mid);//將n-1個盤子從x借助z移動到y
    hanio(1,src,"",dest);//因為中間柱子沒用到,所以可以填""或者填mid,然后將最大的盤子從x直接移動到z
    hanio(n-1,mid,src,dest);//將在中間y柱上的n-1個盤子借助x移動到z
  }
}
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容

  • 遞歸與分治 一、斐波那契(Fibonacci)數列的遞歸實現 他講的一個故事:如果說兔子在出生兩個月后,就有繁殖能...
    我可能是個假開發閱讀 1,052評論 0 10
  • 棧與遞歸 棧還有一個重要應用是在程序設計語言中實現遞歸。一個直接調用自己或通過一系列的調用語句間接的調用自己的函數...
    Mr_Bluyee閱讀 3,120評論 0 1
  • 當一個問題規模比較大且不易求解的時候,就可以考慮將問題分成幾個小的模塊,逐一求解。分治思想和遞歸算是有親兄弟的關系...
    NotFunGuy閱讀 1,254評論 0 5
  • 前置文章:遞歸算法:www.lxweimin.com/p/703069f3ba3f . 漢諾塔問題是來源于印度傳...
    郎小凱閱讀 780評論 0 1
  • 遞歸介紹 本來預算此章節是繼續寫快速排序的,然而編寫快速排序往往是遞歸來寫的,并且遞歸可能不是那么好理解,于是就有...
    Java3y閱讀 16,127評論 7 20