簡體   English   中英

這個遞歸 function 如何在 C++ 中工作?

[英]How does this recursive function work in C++?

I'm currently taking a class for c++ and we are learning about recursion and in class my professor used this function as an example of recursion, the function is meant to return the smallest digit in a number and is:

int smallD(int n) {
    if (n < 10) return n;
    int x = smallD(n / 10);
    if (x < n % 10) return x;
    else return n % 10;
}

我對將 x 設置為遞歸調用的工作原理感到困惑,function 會不會一直運行 n / 10 直到 n < 10,我真的不明白這個概念,並且可以使用一些指針來說明這個 function 是如何工作的。

這里有一些有助於理解遞歸的東西。 添加打印語句以觀察代碼,因為它遞歸地調用自身並傳遞和“縮進級別”以提供幫助。

獲取原始的縮小代碼並將其擴展為更具可讀性的內容,並為其添加額外的調試信息。

int smallD(int n, const std::string& indent) {

    cout << indent << "enter: smallD(n=" << n << ")" << endl;

    if (n < 10)  {
        cout << indent << "n < 10 => returning: " << n << endl;
        return n;
    }

    cout << indent << "about to recurse inovking smallD(" << n / 10 << ")" << endl;
    int x = smallD(n / 10, indent+"  "); // grow the indent by 2 spaces
    cout << indent << "return from recursion, result is: " << x << endl;

    cout << indent << "x=" << x << "  n=" << n << " n%10=" << n % 10 << endl;

    if (x < n % 10) {
        cout << indent << "x is less than n%10, returning: " << x << endl;
        return x;
    }

    cout << indent << "x is greater than or equal n%10, returning: " << n%10 << endl;
    return n % 10;
}

讓我們通過調用smallD(8942468, "")來嘗試一下

enter: smallD(n=8942468)
about to recurse inovking smallD(894246)
  enter: smallD(n=894246)
  about to recurse inovking smallD(89424)
    enter: smallD(n=89424)
    about to recurse inovking smallD(8942)
      enter: smallD(n=8942)
      about to recurse inovking smallD(894)
        enter: smallD(n=894)
        about to recurse inovking smallD(89)
          enter: smallD(n=89)
          about to recurse inovking smallD(8)
            enter: smallD(n=8)
            n < 10 => returning: 8
          return from recursion, result is: 8
          x=8  n=89 n%10=9
          x is less than n%10, returning: 8
        return from recursion, result is: 8
        x=8  n=894 n%10=4
        x is greater than or equal n%10, returning: 4
      return from recursion, result is: 4
      x=4  n=8942 n%10=2
      x is greater than or equal n%10, returning: 2
    return from recursion, result is: 2
    x=2  n=89424 n%10=4
    x is less than n%10, returning: 2
  return from recursion, result is: 2
  x=2  n=894246 n%10=6
  x is less than n%10, returning: 2
return from recursion, result is: 2
x=2  n=8942468 n%10=8
x is less than n%10, returning: 2    // <== this is the final result

因此,希望這將幫助您了解遞歸的工作原理。

遞歸函數的工作方式與非遞歸函數完全相同。
一個常見的錯誤是嘗試一次考慮所有遞歸調用,就好像它們有一個共享的 state,但理解遞歸 function 的一個關鍵因素實際上是忽略遞歸而只是“本地思考”。

也許通過一個例子可以澄清事情。
讓我們看一下smallD(321) ,用它的值替換 function 主體中的n

smallD(321)

    if (321 < 10) return 321;
    int x = smallD(321 / 10);
    if (x < 321 % 10) return x;
    else return 321 % 10;

第一個條件顯然是錯誤的,為了確定x ,我們需要smallD(321/10) ,即smallD(32)

smallD(32)

    if (32 < 10) return 32;
    int x = smallD(32 / 10);
    if (x < 32 % 10) return x;
    else return 32 % 10;

第一個條件再次為假,所以我們繼續使用smallD(32/10)

smallD(3)

    if (3 < 10) return 3;
    int x = smallD(3 / 10);
    if (x < 3 % 10) return x;
    else return 3 % 10;

現在第一個條件為真,所以這里的結果顯然是3
現在我們可以返回 go 並在每個等待的調用中使用x的值。

smallD(32)

    ...
    if (3 < 32 % 10) return 3;
    else return 32 % 10;

並且3 < 32 % 10是假的,所以我們將32 % 10 - 2返回給調用者。

smallD(321)

    ...
    if (2 < 321 % 10) return 2;
    else return 321 % 10;

並且2 < 321 % 10是假的,所以我們返回321 % 10 ,即1

你的直覺並沒有完全消失:function 確實“繼續運行 n/10 直到 n <10”——但它在對同一個 function 的不同調用中。

您的程序保留了一堆函數調用。 您調用的每個 function 都會在當前堆棧中的所有內容之上放置一個(所謂的)“框架”。 在該框架內“活動”所有“屬於”該 function 調用的變量。 當 function 退出時,它會從堆棧中刪除自己的幀。 當前正在執行的函數的框架位於棧頂。 所以,讓我們看看如果你調用smallD(123)會發生什么:

  • 你從堆棧上的其他東西開始,至少是你的main() 您對 smallD(123) 的調用將smallD(123) smallD(123)框架放在堆棧的頂部。 該幀包含變量n = 123 讓我們將此幀堆棧稱為幀A

  • 由於n >= 10 ,您的smallD(123)調用smallD(123 / 10) ,即smallD(12) (整數除法基本上在 C++ 中截斷)。 因此,您將另一個框架放在堆棧頂部。 此堆棧幀對應於smallD(12)並包含變量n = 12 我們稱這個堆棧幀B

  • 同樣, n >=10 ,所以調用smallD(12 / 10) (即smallD(1) )發生。 為這個新調用創建了一個堆棧幀(稱為C ),其中n = 1

  • 現在n < 10成立! 最后的 function 調用(在堆棧幀C中)返回值1並刪除其自己的堆棧幀(幀C )。

  • 堆棧幀B (其中n = 12 )現在位於頂部,並且smallD(12)繼續執行。 由於堆棧幀C已返回1 ,因此堆棧幀B現在包含x = 1 比較(x < n % 10)為真 ( 1 < 2 ),堆棧幀B返回x = 1

  • 同樣的情況再次發生在堆棧幀A中,堆棧幀A (對應於我們最初的smallD(123)調用)返回1

所以你看,確實發生了“直到 n < 10 的除法”,但是在不同的(遞歸!)調用smallD

您可以將幾乎每個遞歸調用分解為基本案例(最簡單的)和遞歸案例 在這種情況下:

  • 基本情況是數字只有一個數字:這意味着小於 10(這是第一個有兩位數字的數字)。
     if (n < 10) return n;
    沒有其他數字可以比較,所以直接返回。
  • 遞歸情況是不是基礎的任何其他情況。 現在我們的數字有兩個或多個數字,所以:
    • 並通過遞歸調用x = smallD(n / 10)了解除最后一位之外的所有數字中的最小數字是什么
    • 然后我們將結果與我們之前未包含的最后一位數字進行比較,並返回最小值。

為了更好地理解調用背后的過程,您可以打印一些信息並觀察它。 嘗試這樣的事情:

int smallD(int n) {
    if (n < 10){
      std::cout << "Base case. Return " << n << std::endl;
      return n;
    }
    std::cout << "Recursive case: " << n << std::endl;
    std::cout << "Compare " << n % 10 << " with smallest of " << n/10 << std::endl;
    int x = smallD(n / 10);
    int ret;
    if (x < n % 10) ret = x;
    else ret = n % 10;
    std::cout << "Smallest of " << n << " is " << ret << std::endl;
    return ret;
}

暫無
暫無

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

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