return 语句在递归函数中究竟是如何工作的?
How exactly does return statement work in recursive function?
我很难解决任何二叉树问题,而且大多数问题都是递归的,所以我决定绕道而行,重新从基础开始。这基本上是预序遍历列表。如果至少有 1 个节点,我已经确定了两种可以给我正确结果的方法,但我不确定这两者之间的内在差异。可以看到,一个return遇到null时是列表,另一个return遇到null时是null:
public List<Integer> getList1(TreeNode root) {
return getList1Helper(root, new ArrayList<>());
}
private List<Integer> getList1Helper(TreeNode root, List<Integer> list) {
if (root == null) {
return list;
}
list.add(root.val);
getList1Helper(root.left, list);
getList1Helper(root.right, list);
return list;
}
public List<Integer> getList2(TreeNode root) {
return getList2Helper(root, new ArrayList<>());
}
private List<Integer> getList2Helper(TreeNode root, List<Integer> list) {
if (root == null) {
return null; // difference denoted *
}
list.add(root.val);
getList2Helper(root.left, list);
getList2Helper(root.right, list);
return list;
}
有人对此有什么想法吗?另外,为什么函数 return 没有列表并在遇到第一个 null 时停止所有递归?
在辅助函数中 return 什么并不重要,因为您从不使用 returned 值。您也可以将方法定义为 void 并仍然得到正确的结果。
本质上你正在做的(正确的)只是传递列表的引用。
对于你的第二个问题:假设你在二叉树中找到了一个向下三层的空节点。当您 return 在该级别时,您将 return 到该二叉树中的第二级,那里还有其他 getListHelper-calls 等待发生。只有当每个节点都被访问过时,first getListHelper 才会调用 return 列表到 getList 方法。
虽然您 return 在两个版本的代码中有所不同,但两个辅助函数实际上 使用 来自递归调用的值 return:
getList2Helper(root.left, list);
上面进行了递归调用,return有些东西,但是调用者没有对它做任何事情。
唯一一次 returned 值有差异,是在基本情况开始时。这意味着 initial 有差异当调用者将 null
作为第一个参数传递时(表示一棵空树)。第一个版本将 return 一个空数组列表,而第二个版本将 return null
。所以它们并不完全等价。 return 数组列表的那个将更多是我期望得到的空树(即空数组列表,而不是 null
)。
正在清理
由于辅助函数实际上填充了作为参数给出的数组列表,因此它不需要 return 任何东西。主函数可以创建数组列表,将其传递给助手(return 什么都没有),然后 return 填充列表。
像这样:
public List<Integer> getList(TreeNode root) {
List<Integer> list = new ArrayList<>();
getListHelper(root, list);
return list;
}
private void getListHelper(TreeNode root, List<Integer> list) {
if (root == null) {
return;
}
list.add(root.val);
getListHelper(root.left, list);
getListHelper(root.right, list);
}
备选
您可以完全避免使用辅助函数(带有第二个参数),而是从递归调用的列表中填充本地数组列表 return:
public List<Integer> getList(TreeNode root) {
List<Integer> list = new ArrayList<>();
if (root != null) {
list.add(root.val);
list.addAll(getList(root.left));
list.addAll(getList(root.right));
}
return list;
}
这样效率较低,因为值会多次从一个列表复制到另一个列表。使用允许在 O(1) 时间内连接列表的链表实现,您可以克服这个缺点。
我很难解决任何二叉树问题,而且大多数问题都是递归的,所以我决定绕道而行,重新从基础开始。这基本上是预序遍历列表。如果至少有 1 个节点,我已经确定了两种可以给我正确结果的方法,但我不确定这两者之间的内在差异。可以看到,一个return遇到null时是列表,另一个return遇到null时是null:
public List<Integer> getList1(TreeNode root) {
return getList1Helper(root, new ArrayList<>());
}
private List<Integer> getList1Helper(TreeNode root, List<Integer> list) {
if (root == null) {
return list;
}
list.add(root.val);
getList1Helper(root.left, list);
getList1Helper(root.right, list);
return list;
}
public List<Integer> getList2(TreeNode root) {
return getList2Helper(root, new ArrayList<>());
}
private List<Integer> getList2Helper(TreeNode root, List<Integer> list) {
if (root == null) {
return null; // difference denoted *
}
list.add(root.val);
getList2Helper(root.left, list);
getList2Helper(root.right, list);
return list;
}
有人对此有什么想法吗?另外,为什么函数 return 没有列表并在遇到第一个 null 时停止所有递归?
在辅助函数中 return 什么并不重要,因为您从不使用 returned 值。您也可以将方法定义为 void 并仍然得到正确的结果。
本质上你正在做的(正确的)只是传递列表的引用。
对于你的第二个问题:假设你在二叉树中找到了一个向下三层的空节点。当您 return 在该级别时,您将 return 到该二叉树中的第二级,那里还有其他 getListHelper-calls 等待发生。只有当每个节点都被访问过时,first getListHelper 才会调用 return 列表到 getList 方法。
虽然您 return 在两个版本的代码中有所不同,但两个辅助函数实际上 使用 来自递归调用的值 return:
getList2Helper(root.left, list);
上面进行了递归调用,return有些东西,但是调用者没有对它做任何事情。
唯一一次 returned 值有差异,是在基本情况开始时。这意味着 initial 有差异当调用者将 null
作为第一个参数传递时(表示一棵空树)。第一个版本将 return 一个空数组列表,而第二个版本将 return null
。所以它们并不完全等价。 return 数组列表的那个将更多是我期望得到的空树(即空数组列表,而不是 null
)。
正在清理
由于辅助函数实际上填充了作为参数给出的数组列表,因此它不需要 return 任何东西。主函数可以创建数组列表,将其传递给助手(return 什么都没有),然后 return 填充列表。
像这样:
public List<Integer> getList(TreeNode root) {
List<Integer> list = new ArrayList<>();
getListHelper(root, list);
return list;
}
private void getListHelper(TreeNode root, List<Integer> list) {
if (root == null) {
return;
}
list.add(root.val);
getListHelper(root.left, list);
getListHelper(root.right, list);
}
备选
您可以完全避免使用辅助函数(带有第二个参数),而是从递归调用的列表中填充本地数组列表 return:
public List<Integer> getList(TreeNode root) {
List<Integer> list = new ArrayList<>();
if (root != null) {
list.add(root.val);
list.addAll(getList(root.left));
list.addAll(getList(root.right));
}
return list;
}
这样效率较低,因为值会多次从一个列表复制到另一个列表。使用允许在 O(1) 时间内连接列表的链表实现,您可以克服这个缺点。