自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

遞歸函數(shù)什么時候要有返回值,什么時候沒有返回值?

開發(fā) 前端
相信很多同學都會疑惑,遞歸函數(shù)什么時候要有返回值,什么時候沒有返回值,特別是有的時候遞歸函數(shù)返回類型為bool類型。

[[417387]]

相信很多同學都會疑惑,遞歸函數(shù)什么時候要有返回值,什么時候沒有返回值,特別是有的時候遞歸函數(shù)返回類型為bool類型。

那么接下來我通過詳細講解如下兩道題,來回答這個問題:

  • 112.路徑總和
  • 113.路徑總和ii

112. 路徑總和

題目地址:https://leetcode-cn.com/problems/path-sum/

給定一個二叉樹和一個目標和,判斷該樹中是否存在根節(jié)點到葉子節(jié)點的路徑,這條路徑上所有節(jié)點值相加等于目標和。

說明: 葉子節(jié)點是指沒有子節(jié)點的節(jié)點。

示例: 給定如下二叉樹,以及目標和 sum = 22,

返回 true, 因為存在目標和為 22 的根節(jié)點到葉子節(jié)點的路徑 5->4->11->2。

思路

這道題我們要遍歷從根節(jié)點到葉子節(jié)點的的路徑看看總和是不是目標和。

遞歸

可以使用深度優(yōu)先遍歷的方式(本題前中后序都可以,無所謂,因為中節(jié)點也沒有處理邏輯)來遍歷二叉樹

  • 確定遞歸函數(shù)的參數(shù)和返回類型

參數(shù):需要二叉樹的根節(jié)點,還需要一個計數(shù)器,這個計數(shù)器用來計算二叉樹的一條邊之和是否正好是目標和,計數(shù)器為int型。

再來看返回值,遞歸函數(shù)什么時候需要返回值?什么時候不需要返回值?這里總結(jié)如下三點:

  • 如果需要搜索整顆二叉樹且不用處理遞歸返回值,遞歸函數(shù)就不要返回值。(這種情況就是本文下半部分介紹的113.路徑總和ii)
  • 如果需要搜索整顆二叉樹且需要處理遞歸返回值,遞歸函數(shù)就需要返回值。(這種情況我們在236. 二叉樹的最近公共祖先介紹)
  • 如果要搜索其中一條符合條件的路徑,那么遞歸一定需要返回值,因為遇到符合條件的路徑了就要及時返回。(本題的情況)

而本題我們要找一條符合條件的路徑,所以遞歸函數(shù)需要返回值,及時返回,那么返回類型是什么呢?

如圖所示:

112.路徑總和

圖中可以看出,遍歷的路線,并不要遍歷整棵樹,所以遞歸函數(shù)需要返回值,可以用bool類型表示。

所以代碼如下:

  1. bool traversal(treenode* cur, int count) // 注意函數(shù)的返回類型 
  • 確定終止條件

首先計數(shù)器如何統(tǒng)計這一條路徑的和呢?

不要去累加然后判斷是否等于目標和,那么代碼比較麻煩,可以用遞減,讓計數(shù)器count初始為目標和,然后每次減去遍歷路徑節(jié)點上的數(shù)值。

如果最后count == 0,同時到了葉子節(jié)點的話,說明找到了目標和。

如果遍歷到了葉子節(jié)點,count不為0,就是沒找到。

遞歸終止條件代碼如下:

  1. if (!cur->left && !cur->right && count == 0) return true; // 遇到葉子節(jié)點,并且計數(shù)為0 
  2. if (!cur->left && !cur->rightreturn false; // 遇到葉子節(jié)點而沒有找到合適的邊,直接返回 
  • 確定單層遞歸的邏輯

因為終止條件是判斷葉子節(jié)點,所以遞歸的過程中就不要讓空節(jié)點進入遞歸了。

遞歸函數(shù)是有返回值的,如果遞歸函數(shù)返回true,說明找到了合適的路徑,應該立刻返回。

代碼如下:

  1. if (cur->left) { // 左 (空節(jié)點不遍歷) 
  2.     // 遇到葉子節(jié)點返回true,則直接返回true 
  3.     if (traversal(cur->leftcount - cur->left->val)) return true; // 注意這里有回溯的邏輯 
  4. if (cur->right) { // 右 (空節(jié)點不遍歷) 
  5.     // 遇到葉子節(jié)點返回true,則直接返回true 
  6.     if (traversal(cur->rightcount - cur->right->val)) return true; // 注意這里有回溯的邏輯 
  7. return false

以上代碼中是包含著回溯的,沒有回溯,如何后撤重新找另一條路徑呢。

回溯隱藏在traversal(cur->left, count - cur->left->val)這里, 因為把count - cur->left->val 直接作為參數(shù)傳進去,函數(shù)結(jié)束,count的數(shù)值沒有改變。

為了把回溯的過程體現(xiàn)出來,可以改為如下代碼:

  1. if (cur->left) { // 左 
  2.     count -= cur->left->val; // 遞歸,處理節(jié)點; 
  3.     if (traversal(cur->leftcount)) return true
  4.     count += cur->left->val; // 回溯,撤銷處理結(jié)果 
  5. if (cur->right) { // 右 
  6.     count -= cur->right->val; 
  7.     if (traversal(cur->rightcount)) return true
  8.     count += cur->right->val; 
  9. return false

整體代碼如下:

  1. class solution { 
  2. private: 
  3.     bool traversal(treenode* cur, int count) { 
  4.         if (!cur->left && !cur->right && count == 0) return true; // 遇到葉子節(jié)點,并且計數(shù)為0 
  5.         if (!cur->left && !cur->rightreturn false; // 遇到葉子節(jié)點直接返回 
  6.  
  7.         if (cur->left) { // 左 
  8.             count -= cur->left->val; // 遞歸,處理節(jié)點; 
  9.             if (traversal(cur->leftcount)) return true
  10.             count += cur->left->val; // 回溯,撤銷處理結(jié)果 
  11.         } 
  12.         if (cur->right) { // 右 
  13.             count -= cur->right->val; // 遞歸,處理節(jié)點; 
  14.             if (traversal(cur->rightcount)) return true
  15.             count += cur->right->val; // 回溯,撤銷處理結(jié)果 
  16.         } 
  17.         return false
  18.     } 
  19.  
  20. public
  21.     bool haspathsum(treenode* root, int sum) { 
  22.         if (root == nullreturn false
  23.         return traversal(root, sum - root->val); 
  24.     } 
  25. }; 

以上代碼精簡之后如下:

  1. class solution { 
  2. public
  3.     bool haspathsum(treenode* root, int sum) { 
  4.         if (root == nullreturn false
  5.         if (!root->left && !root->right && sum == root->val) { 
  6.             return true
  7.         } 
  8.         return haspathsum(root->leftsum - root->val) || haspathsum(root->rightsum - root->val); 
  9.     } 
  10. }; 

是不是發(fā)現(xiàn)精簡之后的代碼,已經(jīng)完全看不出分析的過程了,所以我們要把題目分析清楚之后,在追求代碼精簡。 這一點我已經(jīng)強調(diào)很多次了!

迭代

如果使用棧模擬遞歸的話,那么如果做回溯呢?

此時棧里一個元素不僅要記錄該節(jié)點指針,還要記錄從頭結(jié)點到該節(jié)點的路徑數(shù)值總和。

c++就我們用pair結(jié)構來存放這個棧里的元素。

定義為:pair

這個為棧里的一個元素。

如下代碼是使用棧模擬的前序遍歷,如下:(詳細注釋)

  1. class solution { 
  2.  
  3. public
  4.     bool haspathsum(treenode* root, int sum) { 
  5.         if (root == nullreturn false
  6.         // 此時棧里要放的是pair<節(jié)點指針,路徑數(shù)值> 
  7.         stack<pair<treenode*, int>> st; 
  8.         st.push(pair<treenode*, int>(root, root->val)); 
  9.         while (!st.empty()) { 
  10.             pair<treenode*, int> node = st.top(); 
  11.             st.pop(); 
  12.             // 如果該節(jié)點是葉子節(jié)點了,同時該節(jié)點的路徑數(shù)值等于sum,那么就返回true 
  13.             if (!node.first->left && !node.first->right && sum == node.secondreturn true
  14.  
  15.             // 右節(jié)點,壓進去一個節(jié)點的時候,將該節(jié)點的路徑數(shù)值也記錄下來 
  16.             if (node.first->right) { 
  17.                 st.push(pair<treenode*, int>(node.first->right, node.second + node.first->right->val)); 
  18.             } 
  19.  
  20.             // 左節(jié)點,壓進去一個節(jié)點的時候,將該節(jié)點的路徑數(shù)值也記錄下來 
  21.             if (node.first->left) { 
  22.                 st.push(pair<treenode*, int>(node.first->left, node.second + node.first->left->val)); 
  23.             } 
  24.         } 
  25.         return false
  26.     } 
  27. }; 

如果大家完全理解了本地的遞歸方法之后,就可以順便把leetcode上113. 路徑總和ii做了。

113. 路徑總和ii

題目地址:https://leetcode-cn.com/problems/path-sum-ii/

給定一個二叉樹和一個目標和,找到所有從根節(jié)點到葉子節(jié)點路徑總和等于給定目標和的路徑。

說明: 葉子節(jié)點是指沒有子節(jié)點的節(jié)點。

示例: 給定如下二叉樹,以及目標和 sum = 22,

思路

113.路徑總和ii要遍歷整個樹,找到所有路徑,所以遞歸函數(shù)不要返回值!

如圖:

113.路徑總和ii

為了盡可能的把細節(jié)體現(xiàn)出來,我寫出如下代碼(這份代碼并不簡潔,但是邏輯非常清晰)

  1. class solution { 
  2. private: 
  3.     vector<vector<int>> result; 
  4.     vector<int> path; 
  5.     // 遞歸函數(shù)不需要返回值,因為我們要遍歷整個樹 
  6.     void traversal(treenode* cur, int count) { 
  7.         if (!cur->left && !cur->right && count == 0) { // 遇到了葉子節(jié)點且找到了和為sum的路徑 
  8.             result.push_back(path); 
  9.             return
  10.         } 
  11.  
  12.         if (!cur->left && !cur->rightreturn ; // 遇到葉子節(jié)點而沒有找到合適的邊,直接返回 
  13.  
  14.         if (cur->left) { // 左 (空節(jié)點不遍歷) 
  15.             path.push_back(cur->left->val); 
  16.             count -= cur->left->val; 
  17.             traversal(cur->leftcount);    // 遞歸 
  18.             count += cur->left->val;        // 回溯 
  19.             path.pop_back();                // 回溯 
  20.         } 
  21.         if (cur->right) { // 右 (空節(jié)點不遍歷) 
  22.             path.push_back(cur->right->val); 
  23.             count -= cur->right->val; 
  24.             traversal(cur->rightcount);   // 遞歸 
  25.             count += cur->right->val;       // 回溯 
  26.             path.pop_back();                // 回溯 
  27.         } 
  28.         return ; 
  29.     } 
  30.  
  31. public
  32.     vector<vector<int>> pathsum(treenode* root, int sum) { 
  33.         result.clear(); 
  34.         path.clear(); 
  35.         if (root == nullreturn result; 
  36.         path.push_back(root->val); // 把根節(jié)點放進路徑 
  37.         traversal(root, sum - root->val); 
  38.         return result; 
  39.     } 
  40. }; 

至于113. 路徑總和ii 的迭代法我并沒有寫,用迭代方式記錄所有路徑比較麻煩,也沒有必要,如果大家感興趣的話,可以再深入研究研究。

總結(jié)

本篇通過leetcode上112. 路徑總和 和 113. 路徑總和ii 詳細的講解了 遞歸函數(shù)什么時候需要返回值,什么不需要返回值。

這兩道題目是掌握這一知識點非常好的題目,大家看完本篇文章再去做題,就會感受到搜索整棵樹和搜索某一路徑的差別。

對于112. 路徑總和,我依然給出了遞歸法和迭代法,這種題目其實用迭代法會復雜一些,能掌握遞歸方式就夠了!

本文轉(zhuǎn)載自微信公眾號「代碼隨想錄」,可以通過以下二維碼關注。轉(zhuǎn)載本文請聯(lián)系代碼隨想錄公眾號。

 

責任編輯:武曉燕 來源: 代碼隨想錄
相關推薦

2009-11-17 16:16:59

PHP遞歸函數(shù)

2017-06-28 15:06:51

PythonLambda函數(shù)

2020-05-12 11:25:50

MySQLES數(shù)據(jù)庫

2017-05-15 09:55:07

2010-07-21 10:32:05

Perl函數(shù)返回值

2015-07-08 15:55:01

NSStringcopystrong

2013-11-28 16:03:24

2012-09-24 10:20:39

JavaScriptJS

2022-05-19 10:27:34

機器學習人工智能

2024-08-05 01:22:16

2009-12-25 17:21:13

ADO返回值

2010-07-09 13:20:37

HART協(xié)議

2009-12-07 11:11:41

WCF返回值

2025-01-17 10:52:26

定義函數(shù)編程Python

2015-03-02 14:44:48

AngularJS jQuery超越

2021-01-30 19:59:37

性能項目開源

2015-02-01 09:45:46

2023-06-06 16:54:00

2017-04-05 21:43:08

MQ互聯(lián)網(wǎng)架構

2011-10-18 16:41:23

編程
點贊
收藏

51CTO技術棧公眾號