成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

遞歸就這么簡(jiǎn)單

dreamtecher / 2539人閱讀

摘要:那么,有了循環(huán),為什么還要用遞歸呢在某些情況下費(fèi)波納切數(shù)列,漢諾塔,使用遞歸會(huì)比循環(huán)簡(jiǎn)單很多很多話說(shuō)多了也無(wú)益,讓我們來(lái)感受一下遞歸吧。

遞歸介紹

本來(lái)預(yù)算此章節(jié)是繼續(xù)寫(xiě)快速排序的,然而編寫(xiě)快速排序往往是遞歸來(lái)寫(xiě)的,并且遞歸可能不是那么好理解,于是就有了這篇文章。

在上面提到了遞歸這么一個(gè)詞,遞歸在程序語(yǔ)言中簡(jiǎn)單的理解是:方法自己調(diào)用自己

遞歸其實(shí)和循環(huán)是非常像的,循環(huán)可以改寫(xiě)成遞歸,遞歸未必能改寫(xiě)成循環(huán),這是一個(gè)充分不必要的條件。

那么,有了循環(huán),為什么還要用遞歸呢??在某些情況下(費(fèi)波納切數(shù)列,漢諾塔),使用遞歸會(huì)比循環(huán)簡(jiǎn)單很多很多

話說(shuō)多了也無(wú)益,讓我們來(lái)感受一下遞歸吧。

我們初學(xué)編程的時(shí)候肯定會(huì)做過(guò)類(lèi)似的練習(xí):

1+2+3+4+....+100(n)求和

給出一個(gè)數(shù)組,求該數(shù)組內(nèi)部的最大值

我們要記住的是,想要用遞歸必須知道兩個(gè)條件:

遞歸出口(終止遞歸的條件)

遞歸表達(dá)式(規(guī)律)

技巧:在遞歸中常常是將問(wèn)題切割成兩個(gè)部分(1和整體的思想),這能夠讓我們快速找到遞歸表達(dá)式(規(guī)律)

一、求和

如果我們使用for循環(huán)來(lái)進(jìn)行求和1+2+3+4+....+100,那是很簡(jiǎn)單的:

    int sum = 0;
    for (int i = 1; i <= 100; i++) {

        sum = sum + i;

    }
    System.out.println("公眾號(hào):Java3y:" + sum);

前面我說(shuō)了,for循環(huán)都可以使用遞歸來(lái)進(jìn)行改寫(xiě),而使用遞歸必須要知道兩個(gè)條件:1、遞歸出口,2、遞歸表達(dá)式(規(guī)律)

首先,我們來(lái)找出它的規(guī)律:1+2+3+...+n,這是一個(gè)求和的運(yùn)算,那么我們可以假設(shè)X=1+2+3+...+n,可以將1+2+3+...+(n-1)看成是一個(gè)整體。而這個(gè)整體做的事又和我們的初始目的(求和)相同。以我們的高中數(shù)學(xué)知識(shí)我們又可以將上面的式子看成X=sum(n-1)+n

好了,我們找到我們的遞歸表達(dá)式(規(guī)律),它就是sum(n-1)+n,那遞歸出口呢,這個(gè)題目的遞歸出口就有很多了,我列舉一下:

如果n=1時(shí),那么就返回1

如果n=2時(shí),那么就返回3(1+2)

如果n=3時(shí),那么就返回6(1+2+3)

當(dāng)然了,我肯定是使用一個(gè)最簡(jiǎn)單的遞歸出口了:if(n=1) return 1

遞歸表達(dá)式和遞歸出口我們都找到了,下面就代碼演示:

遞歸出口為1:

    public static void main(String[] args) {
        System.out.println("公眾號(hào):Java3y:" + sum(100));
    }

    /**
     *
     * @param n 要加到的數(shù)字,比如題目的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("公眾號(hào):Java3y:" + sum(100));
    }

    /**
     *
     * @param n 要加到的數(shù)字,比如題目的100
     * @return
     */
    public static int sum(int n) {

        //如果遞歸出口為4,(1+2+3+4)
        if (n == 4) {
            return 10;
        } else {
            return sum(n - 1) + n;
        }
    }

結(jié)果都是一樣的。

二、數(shù)組內(nèi)部的最大值

如果使用的是循環(huán),那么我們通常這樣實(shí)現(xiàn):

    int[] arrays = {2, 3, 4, 5, 1, 5, 2, 9, 5, 6, 8, 3, 2};

    //將數(shù)組的第一個(gè)假設(shè)是最大值
    int max = arrays[0];

    for (int i = 1; i < arrays.length; i++) {

        if (arrays[i] > max) {
            max = arrays[i];
        }
    }

    System.out.println("公眾號(hào):Java3y:" + max);

那如果我們用遞歸的話,那怎么用弄呢?首先還是先要找到遞歸表達(dá)式(規(guī)律)和遞歸出口

我們又可以運(yùn)用1和整體的思想來(lái)找到規(guī)律

將數(shù)組第一個(gè)數(shù)->2與數(shù)組后面的數(shù)->{3, 4, 5, 1, 5, 2, 9, 5, 6, 8, 3, 2}進(jìn)行切割,將數(shù)組后面的數(shù)看成是一個(gè)整體X={3, 4, 5, 1, 5, 2, 9, 5, 6, 8, 3, 2},那么我們就可以看成是第一個(gè)數(shù)和一個(gè)整體進(jìn)行比較if(2>X) return 2 else(2

而我們要做的就是找出這個(gè)整體的最大值與2進(jìn)行比較。找出整體的最大值又是和我們的初始目的(找出最大值)是一樣的

也就可以寫(xiě)成if( 2>findMax() )return 2 else return findMax()

遞歸出口,如果數(shù)組只有1個(gè)元素時(shí),那么這個(gè)數(shù)組最大值就是它了。

使用到數(shù)組的時(shí)候,我們通常為數(shù)組設(shè)定左邊界和右邊界,這樣比較好地進(jìn)行切割

L表示左邊界,往往表示的是數(shù)組第一個(gè)元素,也就會(huì)賦值為0(角標(biāo)為0是數(shù)組的第一個(gè)元素)

R表示右邊界,往往表示的是數(shù)組的長(zhǎng)度,也就會(huì)賦值為arrays.length-1(長(zhǎng)度-1在角標(biāo)中才是代表最后一個(gè)元素)

那么可以看看我們遞歸的寫(xiě)法了:


  public static void main(String[] args) {

        int[] arrays = {2, 3, 4, 5, 1, 5, 2, 9, 5, 6, 8, 3, 1};

        System.out.println("公眾號(hào):Java3y:" + findMax(arrays, 0, arrays.length - 1));


    }
  

    /**
     * 遞歸,找出數(shù)組最大的值
     * @param arrays 數(shù)組
     * @param L      左邊界,第一個(gè)數(shù)
     * @param R      右邊界,數(shù)組的長(zhǎng)度
     * @return
     */

    public static int findMax(int[] arrays, int L, int R) {

        //如果該數(shù)組只有一個(gè)數(shù),那么最大的就是該數(shù)組第一個(gè)值了
        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;
            }
        }

    }
三、冒泡排序遞歸寫(xiě)法

在冒泡排序章節(jié)中給出了C語(yǔ)言的遞歸實(shí)現(xiàn)冒泡排序,那么現(xiàn)在我們已經(jīng)使用遞歸的基本思路了,我們使用Java來(lái)重寫(xiě)一下看看:

冒泡排序:倆倆交換,在第一趟排序中能夠?qū)⒆畲笾蹬诺阶詈竺?,外層循環(huán)控制排序趟數(shù),內(nèi)層循環(huán)控制比較次數(shù)

以遞歸的思想來(lái)進(jìn)行改造:

當(dāng)?shù)谝惶伺判蚝?,我們可以將?shù)組最后一位(R)和數(shù)組前面的數(shù)(L,R-1)進(jìn)行切割,數(shù)組前面的數(shù)(L,R-1)看成是一個(gè)整體,這個(gè)整體又是和我們的初始目的(找出最大值,與當(dāng)前趟數(shù)的末尾處交換)是一樣的

遞歸出口:當(dāng)只有一個(gè)元素時(shí),即不用比較了: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("公眾號(hào):Java3y:" + arrays);


    }

    public static void bubbleSort(int[] arrays, int L, int R) {

        int temp;

        //如果只有一個(gè)元素了,那什么都不用干
        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;
                }
            }

            //第一趟排序后已經(jīng)將最大值放到數(shù)組最后面了

            //接下來(lái)是排序"整體"的數(shù)據(jù)了
            bubbleSort(arrays, L, R - 1);

        }
    }

四、斐波那契數(shù)列

接觸過(guò)C語(yǔ)言的同學(xué)很可能就知道什么是費(fèi)波納切數(shù)列了,因?yàn)橥鼍毩?xí)題的時(shí)候它就會(huì)出現(xiàn),它也是遞歸的典型應(yīng)用。

菲波那切數(shù)列長(zhǎng)這個(gè)樣子:{1 1 2 3 5 8 13 21 34 55..... n }

數(shù)學(xué)好的同學(xué)可能很容易就找到規(guī)律了:前兩項(xiàng)之和等于第三項(xiàng)

例如:

     1 + 1 = 2
    2 + 3 = 5
    13 + 21 = 34

如果讓我們求出第n項(xiàng)是多少,那么我們就可以很簡(jiǎn)單寫(xiě)出對(duì)應(yīng)的遞歸表達(dá)式了:Z = (n-2) + (n-1)

遞歸出口在本題目是需要有兩個(gè)的,因?yàn)?strong>它是前兩項(xiàng)加起來(lái)才得出第三項(xiàng)的值

同樣地,那么我們的遞歸出口可以寫(xiě)成這樣:if(n==1) retrun 1 if(n==2) return 2

下面就來(lái)看一下完整的代碼吧:

    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("公眾號(hào):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));
        }

    }
五、漢諾塔算法

圖片來(lái)源百度百科:

玩漢諾塔的規(guī)則很簡(jiǎn)單:

有三根柱子,原始裝滿大小不一的盤(pán)子的柱子我們稱(chēng)為A,還有兩根空的柱子,我們分別稱(chēng)為B和C(任選)

最終的目的就是將A柱子的盤(pán)子全部移到C柱子中

移動(dòng)的時(shí)候有個(gè)規(guī)則:一次只能移動(dòng)一個(gè)盤(pán)子,小的盤(pán)子不能在大的盤(pán)子上面(反過(guò)來(lái):大的盤(pán)子不能在小的盤(pán)子上面)

我們下面就來(lái)玩一下:

只有一個(gè)盤(pán)子:

A柱子的盤(pán)子直接移動(dòng)到C柱子中

完成游戲

只有兩個(gè)盤(pán)子:

將A柱子上的盤(pán)子移動(dòng)到B柱子中

將A柱子上的盤(pán)子移動(dòng)到C柱子中

最后將在B柱子的盤(pán)子移動(dòng)到C柱子盤(pán)子中

完成游戲

只有三個(gè)盤(pán)子:

將A柱子的盤(pán)子移動(dòng)到C柱子中

將A柱子上的盤(pán)子移動(dòng)到B柱子中

將C柱子盤(pán)子移動(dòng)到B柱子盤(pán)子中

將A柱子的盤(pán)子移動(dòng)到C柱子中

將B柱子的盤(pán)子移動(dòng)到A柱子中

將B柱子的盤(pán)子移動(dòng)到C柱子中

最后將A柱子的盤(pán)子移動(dòng)到C柱子中

完成游戲


.......................

從前三次玩法中我們就可以發(fā)現(xiàn)的規(guī)律:

想要將最大的盤(pán)子移動(dòng)到C柱子,就必須將其余的盤(pán)子移到B柱子處(借助B柱子將最大盤(pán)子移動(dòng)到C柱子中[除了最大盤(pán)子,將所有盤(pán)子移動(dòng)到B柱子中])[遞歸表達(dá)式]

當(dāng)C柱子有了最大盤(pán)子時(shí),所有的盤(pán)子在B柱子。現(xiàn)在的目的就是借助A柱子將B柱子的盤(pán)子都放到C柱子中(和上面是一樣的,已經(jīng)發(fā)生遞歸了)

當(dāng)只有一個(gè)盤(pán)子時(shí),就可以直接移動(dòng)到C柱子了(遞歸出口)

A柱子稱(chēng)之為起始柱子,B柱子稱(chēng)之為中轉(zhuǎn)柱子,C柱子稱(chēng)之為目標(biāo)柱子

從上面的描述我們可以發(fā)現(xiàn),起始柱子、中轉(zhuǎn)柱子它們的角色是會(huì)變的(A柱子開(kāi)始是起始柱子,第二輪后就變成了中轉(zhuǎn)柱子了。B柱子開(kāi)始是目標(biāo)柱子,第二輪后就變成了起始柱子??傊?strong>起始柱子、中轉(zhuǎn)柱子的角色是不停切換的)

簡(jiǎn)單來(lái)說(shuō)就分成三步:

把 n-1 號(hào)盤(pán)子移動(dòng)到中轉(zhuǎn)柱子

把最大盤(pán)子從起點(diǎn)移到目標(biāo)柱子

把中轉(zhuǎn)柱子的n-1號(hào)盤(pán)子也移到目標(biāo)柱子

那么就可以寫(xiě)代碼測(cè)試一下了(回看上面玩的過(guò)程):

    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("公眾號(hào):Java3y" );


    }

    /**
     * 漢諾塔
     * @param n n個(gè)盤(pán)子
     * @param start 起始柱子
     * @param transfer 中轉(zhuǎn)柱子
     * @param target 目標(biāo)柱子
     */
    public static void hanoi(int n, char start, char transfer, char target) {


        //只有一個(gè)盤(pán)子,直接搬到目標(biāo)柱子
        if (n == 1) {
            System.out.println(start + "---->" + target);
        } else {

            //起始柱子借助目標(biāo)柱子將盤(pán)子都移動(dòng)到中轉(zhuǎn)柱子中(除了最大的盤(pán)子)
            hanoi(n - 1, start, target, transfer);
            System.out.println(start + "---->" + target);

            //中轉(zhuǎn)柱子借助起始柱子將盤(pán)子都移動(dòng)到目標(biāo)柱子中
            hanoi(n - 1, transfer, start, target);

        }
    }

我們來(lái)測(cè)試一下看寫(xiě)得對(duì)不對(duì):

參考資料:

https://www.zhihu.com/question/24385418

六、總結(jié)

遞歸的確是一個(gè)比較難理解的東西,好幾次都把我繞進(jìn)去了....

要使用遞歸首先要知道兩件事:

遞歸出口(終止遞歸的條件)

遞歸表達(dá)式(規(guī)律)

在遞歸中常常用”整體“的思想,在漢諾塔例子中也不例外:將最大盤(pán)的盤(pán)子看成1,上面的盤(pán)子看成一個(gè)整體。那么我們?cè)谘菟愕臅r(shí)候就很清晰了:將”整體“搬到B柱子,將最大的盤(pán)子搬到C柱子,最后將B柱子的盤(pán)子搬到C柱子中

因?yàn)槲覀內(nèi)四X無(wú)法演算那么多的步驟,遞歸是用計(jì)算機(jī)來(lái)干的,只要我們找到了遞歸表達(dá)式和遞歸出口就要相信計(jì)算機(jī)能幫我們搞掂。

在編程語(yǔ)言中,遞歸的本質(zhì)是方法自己調(diào)用自己,只是參數(shù)不一樣罷了。

最后,我們來(lái)看一下如果是5個(gè)盤(pán)子,要運(yùn)多少次才能運(yùn)完:

PS:如果有更好的理解方法,或者我理解錯(cuò)的地方大家可以在評(píng)論下留言一起交流哦!

如果文章有錯(cuò)的地方歡迎指正,大家互相交流。習(xí)慣在微信看技術(shù)文章,想要獲取更多的Java資源的同學(xué),可以關(guān)注微信公眾號(hào):Java3y

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://hztianpu.com/yun/68863.html

相關(guān)文章

  • 遞歸,這么簡(jiǎn)單

    摘要:簡(jiǎn)而言之,遞歸就是自己調(diào)用自己,但是這個(gè)調(diào)用它是有一定條件的,比如子問(wèn)題須與原始問(wèn)題為同樣的事,且更為簡(jiǎn)單。當(dāng)時(shí),這層遞歸返回,也就是返回到的這層遞歸。這時(shí),至此,遞歸結(jié)束,返回結(jié)果給調(diào)用方。 showImg(https://segmentfault.com/img/bVbvZRe?w=1242&h=735); 什么是遞歸? 維基百科給出了如下定義: 程序調(diào)用自身的編程技巧稱(chēng)為遞歸.遞...

    woshicixide 評(píng)論0 收藏0
  • 快速排序這么簡(jiǎn)單

    摘要:快速排序的介紹來(lái)源百度百科快速排序由在年提出??焖倥判蚴敲嬖嚦霈F(xiàn)的可能性比較高的,也是經(jīng)常會(huì)用到的一種排序,應(yīng)該重點(diǎn)掌握。前面一個(gè)章節(jié)已經(jīng)講了遞歸了,那么現(xiàn)在來(lái)看快速排序就非常簡(jiǎn)單了。 快速排序就這么簡(jiǎn)單 從前面已經(jīng)講解了冒泡排序、選擇排序、插入排序了,本章主要講解的是快速排序,希望大家看完能夠理解并手寫(xiě)出快速排序的代碼,然后就通過(guò)面試了!如果我寫(xiě)得有錯(cuò)誤的地方也請(qǐng)大家在評(píng)論下指出。 ...

    Faremax 評(píng)論0 收藏0
  • Vue一個(gè)案例引發(fā)的遞歸組件的使用

    摘要:今天我們繼續(xù)使用的擼我們的實(shí)戰(zhàn)項(xiàng)目,只有在實(shí)戰(zhàn)中我們才會(huì)領(lǐng)悟更多,光紙上談兵然并卵,繼上篇我們的一個(gè)案例引發(fā)的動(dòng)態(tài)組件與全局事件綁定總結(jié)之后,今天來(lái)聊一聊我們?nèi)绾卧陧?xiàng)目中使用遞歸組件。 今天我們繼續(xù)使用 Vue 的擼我們的實(shí)戰(zhàn)項(xiàng)目,只有在實(shí)戰(zhàn)中我們才會(huì)領(lǐng)悟更多,光紙上談兵然并卵,繼上篇我們的《Vue一個(gè)案例引發(fā)的動(dòng)態(tài)組件與全局事件綁定總結(jié)》 之后,今天來(lái)聊一聊我們?nèi)绾卧陧?xiàng)目中使用遞歸組...

    lucas 評(píng)論0 收藏0
  • 如何實(shí)現(xiàn)一個(gè)沒(méi)有名字的遞歸函數(shù)

    摘要:如果存在這么個(gè)函數(shù),那么我們就可以通過(guò)求解的不動(dòng)點(diǎn)來(lái)求出了。尋找轉(zhuǎn)換函數(shù)的不動(dòng)點(diǎn)找到了轉(zhuǎn)換函數(shù)后,下一步就是確定其不動(dòng)點(diǎn)了,而這個(gè)不動(dòng)點(diǎn)就是我們最終想要的。 本文原發(fā)于個(gè)人博客 遞歸 作為計(jì)算機(jī)科學(xué)中很重要的一個(gè)概念,應(yīng)用范圍非常廣泛。比較重要的數(shù)據(jù)結(jié)構(gòu),像樹(shù)、圖,本身就是遞歸定義的。比較常見(jiàn)的遞歸算法有階乘、斐波那契數(shù)等,它們都是在定義函數(shù)的同時(shí)又引用本身,對(duì)于初學(xué)者來(lái)說(shuō)也比較好理解...

    tinna 評(píng)論0 收藏0
  • 八大基礎(chǔ)排序總結(jié)

    摘要:不斷執(zhí)行這個(gè)操作代碼實(shí)現(xiàn)快速排序用遞歸比較好寫(xiě)如果不太熟悉遞歸的同學(xué)可到遞歸就這么簡(jiǎn)單。 前言 大概花了一周的時(shí)間把八大基礎(chǔ)排序過(guò)了一遍,這篇博文主要是用來(lái)回顧一下八大基礎(chǔ)排序的要點(diǎn)和一些總結(jié)~ 回顧: 冒泡排序就這么簡(jiǎn)單 選擇排序就這么簡(jiǎn)單 插入排序就這么簡(jiǎn)單 快速排序就這么簡(jiǎn)單 歸并排序就這么簡(jiǎn)單 堆排序就這么簡(jiǎn)單 希爾排序就這么簡(jiǎn)單 基數(shù)排序就這么簡(jiǎn)單 總的來(lái)說(shuō):快速排序是用...

    maochunguang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<