溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何用java遞歸實(shí)現(xiàn)1到100的相加

發(fā)布時間:2022-01-06 15:14:49 來源:億速云 閱讀:720 作者:iii 欄目:大數(shù)據(jù)

本篇內(nèi)容介紹了“如何用java遞歸實(shí)現(xiàn)1到100的相加”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

何為遞歸?

所謂遞歸,是指程序在運(yùn)行的過程中調(diào)用自身的行為。

這種行為也不能無限制地進(jìn)行下去,得有個出口,叫做邊界條件,所以,遞歸可以分成三個段:前進(jìn)段、達(dá)到邊界條件,返回段,在這三個段我們都可以做一些事,比如前進(jìn)段對問題規(guī)模進(jìn)行縮小,返回段對結(jié)果進(jìn)行整理。

這么說可能比較抽象,讓我們看一個簡單的案例:

如何用遞歸實(shí)現(xiàn)1到100的相加?

1到100相加使用循環(huán)大家都會解,代碼如下:

public class Sum {
    public static void main(String[] args) {
        System.out.println(sumCircle(1, 100));
    }

    private static int sumCircle(int min, int max) {
        int sum = 0;
        for (int i = min; i <= max; i++) {
            sum += i;
        }
        return sum;
    }
}

那么,如何使用遞歸實(shí)現(xiàn)呢?

如何快速實(shí)現(xiàn)遞歸?

首先,我們要找到這道題的邊界條件,1到100相加,邊界條件可以是1,也可以是100,如果從1開始,那么邊界條件就是100,反之亦然。

找到了邊界條件之后,就是將問題規(guī)??s小,對于這道題,計算1到100相加,那么,能不能先計算1到99相加再把100加上呢?肯定是可以的,這樣問題的規(guī)模就縮小了,直到,問題規(guī)模縮小為1到1相加為止。

OK,讓我們看代碼實(shí)現(xiàn):

private static int sumRecursive(int min, int max) {
    // 邊界條件
    if (min >= max) {
        return min;
    }
    // 問題規(guī)模縮小
    int sum = sumRecursive(min, max - 1);
    // 加上當(dāng)前值
    sum += max;
    // 返回
    return sum;
}

是不是很簡單?還可以更簡單:

private static int sumRecursive2(int min, int max) {
    return min >= max ? min : sumRecursive2(min, max - 1) + max;
}

686?

所以,使用遞歸最重要的就是找到邊界條件,然后讓問題的規(guī)模朝著邊界條件的方向一直縮小,直到達(dá)到邊界條件,最后依次返回即可,這也是快速實(shí)現(xiàn)遞歸的套路。

這么看來,使用遞歸似乎很簡單,但是,它有沒有什么缺點(diǎn)呢?

要了解缺點(diǎn)就得從遞歸的本質(zhì)入手。

遞歸的本質(zhì)是什么?

我們知道,JVM啟動的時候有個參數(shù)叫做-Xss,它不是表示XSS攻擊哈,它是指每個線程可以使用的線程棧的大小。

那么,什么又是線程棧呢?

棧大家都理解了,我們在前面的章節(jié)也學(xué)習(xí)過了,使用棧,可以實(shí)現(xiàn)計算器的功能,非常方便。

線程棧,顧名思義,就是指線程運(yùn)行過程中使用的棧。

那么,線程在運(yùn)行的過程中為什么要使用棧呢?

這就不得不說方法調(diào)用的本質(zhì)了。

舉個簡單的例子:

private static int a(int num) {
    int a = 1;
    return a + b(num);
}

private static int b(int num) {
    int b = 2;
    return c(num) + b;
}

private static int c(int num) {
    int c = 3;
    return c + num;
}

在這段代碼中,方法a() 調(diào)用 方法b(),方法b() 調(diào)用 方法c(),在實(shí)際運(yùn)行的過程中,是這樣處理的:調(diào)用方法a()時,發(fā)現(xiàn)需要調(diào)用方法b()才能返回,那就把方法a()及其狀態(tài)保存到棧中,然后調(diào)用方法b(),同樣地,調(diào)用方法b()時,發(fā)現(xiàn)需要先調(diào)用方法c()才能返回,那就把方法b()及其狀態(tài)入棧,然后調(diào)用方法c(),調(diào)用方法c()時,不需要額外調(diào)用別的方法了,計算完畢返回,返回之后,從棧頂取出方法b()及當(dāng)時的狀態(tài),繼續(xù)運(yùn)行方法b(),方法b()運(yùn)行完畢,返回,再從棧中取出方法a()及當(dāng)時的狀態(tài),計算完畢,方法a()返回,程序等待結(jié)束。

還是上圖吧:

如何用java遞歸實(shí)現(xiàn)1到100的相加

所以,方法調(diào)用的本質(zhì),就是棧的使用。

同理,遞歸的調(diào)用就是方法的調(diào)用,所以,遞歸的調(diào)用,也是棧的使用,不過,這個棧會變得非常大,比如,對于1到100相加,就有99次入棧出棧的操作。

如何用java遞歸實(shí)現(xiàn)1到100的相加

因此,總結(jié)起來,遞歸有以下兩個缺點(diǎn):

  1. 操作耗時,因?yàn)闋可娴酱罅康娜霔3鰲2僮鳎?/p>

  2. 有可能導(dǎo)致線程棧溢出,因?yàn)檫f歸調(diào)用占用了線程棧很大的空間。

那么,我們是不是就不要使用遞歸了呢?

當(dāng)然不是,之所以使用遞歸,就是因?yàn)樗褂闷饋矸浅:唵?,能夠快速地解決我們的問題,合理控制遞歸調(diào)用鏈的長度,就是一個好遞歸。

既然,遞歸調(diào)用的本質(zhì),就是棧的使用,那么,我們能不能自己模擬一個棧,將遞歸調(diào)用改成非遞歸呢?

當(dāng)然可以。

修改遞歸為非遞歸的套路

還是使用上面的例子,現(xiàn)在我們需要把遞歸修改成非遞歸,且不是使用for循環(huán)的那種形式,要怎么實(shí)現(xiàn)呢?

首先,我們要自己模擬一個棧;

然后,找到邊界條件;

最后,朝著邊界條件的方向縮小問題規(guī)模;

OK,上代碼:

private static int sumNonRecursive(int min, int max) {
        int sum = 0;
        // 聲明一個棧
        Stack<Integer> stack = new Stack<Integer>();
        stack.push(max);
        while (!stack.isEmpty()) {
            if (max > min) {
                // 要計算max,先計算max-1
                stack.push(--max);
            } else {
                // 問題規(guī)模縮小到一定程度,計算返回
                sum += stack.pop();
            }
        }
        return sum;
    }

好了,是不是很簡單,其實(shí)跟遞歸的套路是一樣的,只不過改成自己模擬棧來實(shí)現(xiàn)。

這個例子可能不是那么明顯,我們再舉個二叉樹遍歷的例子來看一下。

public class BinaryTree {

    Node root;

    // 插入元素
    void put(int value) {
        if (root == null) {
            root = new Node(value);
        } else {
            Node parent = root;
            while (true) {
                if (value <= parent.value) {
                    if (parent.left == null) {
                        parent.left = new Node(value);
                        return;
                    } else {
                        parent = parent.left;
                    }
                } else {
                    if (parent.right == null) {
                        parent.right = new Node(value);
                        return;
                    } else {
                        parent = parent.right;
                    }
                }

            }
        }
    }

    // 先序遍歷
    void preTraversal(Node x) {
        if (x == null) return;
        System.out.print(x.value + ",");
        preTraversal(x.left);
        preTraversal(x.right);
    }

    static class Node {
        int value;
        Node left;
        Node right;

        public Node(int value) {
            this.value = value;
        }
    }

    public static void main(String[] args) {
        BinaryTree binaryTree = new BinaryTree();
        binaryTree.put(3);
        binaryTree.put(1);
        binaryTree.put(2);
        binaryTree.put(7);
        binaryTree.put(8);
        binaryTree.put(5);
        binaryTree.put(4);
        binaryTree.put(6);
        binaryTree.put(9);
        binaryTree.put(0);

        binaryTree.preTraversal(binaryTree.root);
    }
}

我這里隨手寫了一顆二叉樹,并實(shí)現(xiàn)了其先序遍歷,這個測試用例中的二叉樹長這個樣子:

如何用java遞歸實(shí)現(xiàn)1到100的相加

所以,這個二叉樹的先序遍歷結(jié)果為3,1,0,2,7,5,4,6,8,9,

可以看到,使用遞歸先序遍歷二叉樹非常簡單,而且代碼清晰易懂,那么,它如何修改為非遞歸實(shí)現(xiàn)呢?

首先,我們要自己模擬一個棧;

然后,找到邊界條件,為節(jié)點(diǎn)等于空時;

最后,縮小問題規(guī)模,這里是先把右子樹壓棧,再把左子樹壓棧,因?yàn)橄茸蠛笥遥?/p>

好了,來看代碼實(shí)現(xiàn):

// 先序遍歷非遞歸形式
void nonRecursivePreTraversal(Node x) {
    // 自己模擬一個棧
    Stack<Node> stack = new Stack<Node>();
    stack.push(x);
    while (!stack.isEmpty()) {
        Node tmp = stack.pop();
        // 隱含的邊界條件
        if (tmp != null) {
            System.out.print(tmp.value + ",");
            // 縮小問題規(guī)模
            stack.push(tmp.right);
            stack.push(tmp.left);
        }
    }
}

“如何用java遞歸實(shí)現(xiàn)1到100的相加”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI