在力扣上遇到这么一道题目,本身倒是不难,DFS一遍就解决了,却引发了我对递归函数返回值类型定义的思考。
题目描述和问题介绍
题目和类定义如下图所示:
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node() {}
Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}
Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
我的思考
从直观感受来说,题目要求输出第k大的值,那么定义一个dfs函数,这个函数的返回值应该是int类型比较好吧?
然而看看题解,高赞题解的普遍写法是把dfs函数定义成void,要么是定义一个全局变量用来储存结果,如下图所示
class Solution {
public:
int res;
int kthLargest(TreeNode* root, int k) {
dfs(root,k);
return res;
}
void dfs(TreeNode* root ,int &k) //传引用 这里需要保证所有dfs函数共用一个k
{
if(!root) return;
dfs(root->right,k); //右
k--;
if(!k) res = root->val; //根
dfs(root->left,k); //左
}
};
或者是这样,把结果放在参数列表里随着递归修改,记得参数类型要引用。
class Solution {
public:
int kthLargest(TreeNode* root, int k) {
dfs(root,k);
return res;
}
void dfs(TreeNode* root ,int &k,int& res) //传引用 这里需要保证所有dfs函数共用一个k
{
if(!root) return;
dfs(root->right,k); //右
k--;
if(!k) res = root->val; //根
dfs(root->left,k); //左
}
};
那么可不可以把返回值定义为int呢?也行,不过理解起来就比较抽象了,因为每调用一次递归就会产生一个返回值,也就是说一棵树执行下来要返回好多个int值,这些值哪些是表示答案的呢?哪些值是可以丢掉的呢?或者说答案是这些返回值的和或者运算结果呢?这些都是要考虑的问题,对于本题来说,至多有一个int值是题目答案,其他返回值都应该无视。修改后的代码如下图所示:
class Solution {
public:
int kthLargest(TreeNode* root, int k) {
return dfs(root, k);
}
int dfs(TreeNode* root, int& k) {
if (!root) return -1; // Return -1 to indicate no valid kth element found.
int rightResult = dfs(root->right, k); // Explore right subtree
// If kth element is found in the right subtree, return it.
if (k == 0) return rightResult;
k--; // Decrement k since we are now visiting the current node.
// If kth element is found in the current node, return it.
if (k == 0) return root->val;
return dfs(root->left, k); // Explore left subtree if kth element is not found yet.
}
};
这段代码的解释是,遍历右中左子树,当遍历到第k个值时return答案,否则继续遍历,return dfs(root -> left, k),直到能找到合适的返回值。这么一看流程不是很清晰,但是运行结果确实是一样的。
总结
递归函数的返回值是否应该使用void
取决于函数的具体需求和实现方式。在很多情况下,递归函数并不需要返回值,因为它们的目的是通过递归调用来解决问题,而不是返回特定的结果。在这种情况下,使用void
作为返回值类型是合适的。
使用void
返回值的递归函数的一个优点是简洁性,因为不需要处理返回值,减少了代码的复杂性和潜在的错误,可以把答案放在private变量,或者递归函数的参数中,这样代码的可读性比较高,后续也方便修改和调试。
在二叉搜索树中查找第k大元素,需要找到并返回第k大的元素值。在这种情况下,我们可能会使用类似int
的返回值类型。如上所示,int 返回值是可以达到效果的。
要确定适合的返回值类型,可以考虑以下几个因素:
-
问题的性质:根据问题的性质,确定是否需要返回特定的结果。有些问题需要从递归中获取结果,而有些问题则不需要。
-
函数的目的:确定递归函数的主要目标是什么。如果主要目标是解决问题,并不需要返回特定的结果,那么
void
返回值是合适的。 -
代码的复杂性:有时,在递归函数中处理返回值可能会增加代码的复杂性。如果可以避免使用返回值,可以使代码更简洁易读。
-
全局变量或引用参数:在一些情况下,可以使用全局变量或引用参数来获取递归函数的结果,而不使用返回值。
推荐我这种新手还是不要整花里胡哨的,老老实实把流程和逻辑写的清清楚楚,搞错了还好debug,直观才是最重要的。