簡體   English   中英

理解遞歸的麻煩

[英]Trouble Understanding Recursion

好的,所以我有一個程序:

public class Rec {
    public static void main(String[] args) {
        test(5);
    }
    static void test(int n) {
        if (n > 0) {
        System.out.println(n);
        test(n-1);
        System.out.println(n);
        }
    }

它的輸出是5,4,3,2,1,1,2,3,4,5。 我的問題是,為什么/如何執行第二個println(n)語句? 我認為函數調用會完全切斷它,而是以令我困惑的方式行事。 這不是家庭作業或任何事情,我真的很難理解遞歸是如何工作的。

一旦完成,所有方法調用都會返回到同一個地方。

通過有效地鏈接他們,你得到

 System.out.println(5)
     System.out.println(4)
        System.out.println(3)
            System.out.println(2)
                system.out.println(1)
                   // If is not true now.
                System.out.println(1)
            System.out.println(2)
        System.out.println(3)
     System.out.println(4)
 System.out.println(5)

那有意義嗎?

當您的test方法終止時,它將在上面的堆棧框架中的相同位置恢復。

例如,如果我有一些代碼:

public class Test {
    public static void main(String[] args) {
        System.out.println("A");
        myFunc();
        System.out.println("B");
    }

    public static void myFunc() {
        System.out.println("Do something here");
    }
}

...你希望main兩個printlns都能運行,因為myFunc終止/不會進入無限循環。

這是真的,即使你正在使用遞歸。 您的test方法最終將終止,這意味着必須在某個時間點執行第二個println。


相反,想象一下我們有一個永遠不會終止的“遞歸”函數:

public class Test {
    public static void main(String[] args) {
        test2(5)
    }

    public static void test2(int n) {
        System.out.println("A " + n);
        test(n - 1);
        System.out.println("B " + n);
    }
}

因為test2方法永遠不會終止,所以第二個println絕對沒有辦法執行。 這就是為什么你應該總是設計任何遞歸函數,以便它可以在達到某些條件時終止。

如果我們要理解遞歸, 堆棧的概念是非常重要的。 Stack是LIFO數據結構。 記住每當方法調用發生時,當前狀態被推入堆棧(當前狀態涉及局部變量的值,下一個可執行語句的地址等)。 現在讓我們看看你的問題。

首先發生這種情況

System.out.println(5);
test(n-1); // method call is happening so store state to stack !!!
//stack contents: n=5 and address of next statement
System.out.println(4);
test(n-1);//another state added to stack : n =4
System.out.println(3);
test(n-1);//another state added to stack: n = 3
System.out.println(2);
test(n-1);//another state added to stack : n = 2
System.out.println(1); 
test(n-1);//another state added to stack : n = 1

現在條件if(n> 0)失敗現在返回遞歸階段發生,即控制返回到進行調用的狀態,並記住所有狀態都存儲在堆棧中並且還記得堆棧是LIFO所以現在:

// n = 1 first state in stack
System.out.println(1); 
//n = 2 second state stored in stack
System.out.println(2); 
//n = 3 third state stored in stack
System.out.println(3);
//n = 4 fourth state stored in the stack
System.out.println(4); 
//n = 5 last state stored in stack 
System.out.println(5); 

現在所有的呼叫都已完成,控制權將恢復為主要狀態。

請查看您的代碼:

static void test(int n) {
        if (n > 0) {
        System.out.println(n);
        test(n-1);// this makes the new call to method test() and causes the state to be stored in the stack
        System.out.println(n);// this statement doesn't execute until the recursive call made to test() doesn't return .
        }

HTH :)

假設您的預期結果應為“54321”,那么您的問題就是您的第二個問題

System.out.println(n);

測試方法的第一部分完全符合您的要求:

if (n > 0) {
    System.out.println(n);
    test(n-1);
// System.out.println(n); 
}

結果將是“54321” - 太棒了! 但是,由於測試方法結束時的第二個println方法,實際上是在堆疊輸出(因為Crazy Programmer已經向我們展示了真正的精確度)。 換句話說:你根本不需要第二個println方法來實現你的目標! 這是關於遞歸的奇妙之處:它再次調用test()時會“中斷”,讓第二個println方法“暫時不執行”並再次啟動整個過程。

你可以看到的是,通過使用

if (n > 0)

你正在驗證n大於0(所以至少為1),而(同時)這也是你的休息條件! 那么你的方法實際上要做的是:

  1. 檢查,如果n大於0,
  2. 打印n的值,並且(最后)
  3. 再次呼叫測試方法(用n-1)

如果n達到0,整個“方法堆疊”會自行消解(看看Niels的解決方案)並且不會再返回任何n值。 得到它了?

在遞歸調用自身之后,該方法仍然存在。 一旦處理完這些調用,該方法將返回執行剩余的代碼行。

看看這個: http//ideone.com/zWqP8h

public static void main(String[] args) {
        test(5);
    }
    static void test(int n) {
        if (n > 0) {
        System.out.println("First step :" + n);
        test(n-1);
        System.out.println("Second step :" + n);
        }
    }

可能有助於澄清事情。

我認為你應該跟蹤代碼然后你會得到解決方案。

你的代碼跟蹤這樣的東西。 嘗試調試此行可能會對您有所幫助。

//for n = 5 
void test(int n) {
        if (n > 0) {
        System.out.println(n);
        test(n-1);----->// call of same function
        //for n = 4
        void test(int n) {
            if (n > 0) {
            System.out.println(n);
            test(n-1);-----> 
            //for n = 3
            void test(int n) {
                if (n > 0) {
                System.out.println(n);
                test(n-1);---->
                //for n = 2
                void test(int n) {
                    if (n > 0) {
                    System.out.println(n);
                    test(n-1);---->
                    //for n = 1
                    void test(int n) {
                        if (n > 0) {
                        System.out.println(n);
                        test(n-1); ---->
                        //for n = 0
                        void test(int n) {
                            if (n > 0) {//not satisfy
                                System.out.println(n);
                               test(n-1);
                                System.out.println(n);
                            } 
                            // Till hear you were right but next things you missed.

                        }//again resume of n = 1
                        System.out.println(n);
                        }
                    }//again resume of n = 2
                    System.out.println(n);
                    }
                }//again resume of n = 3
                System.out.println(n);
                }
            }//again resume of n = 4
            System.out.println(n);
            }
        }//again resume of n = 5
        System.out.println(n);
        }
    }//Finish recursion.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM