提问人:ATL_DEV 提问时间:10/2/2023 更新时间:10/6/2023 访问量:78
对尾递归的工作原理感到困惑?
Confused about how tail recursion works?
问:
我正在使用一个从 leetcode 将两个链表添加在一起的示例。列表的顺序相反,生成的列表也必须颠倒。我的代码是递归的,它只比 24% 的提交快。
我正在尝试通过将尾递归应用于我的示例来了解尾递归的工作原理。它将如何改善其空间或时间复杂度?是否只是消除局部变量并将它们作为参数传递?
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int val=0, ListNode next=null) {
* this.val = val;
* this.next = next;
* }
* }
*/
public ListNode AddTwoNumbers(ListNode l1, ListNode l2) {
ListNode digit = null;
int sum = l1.val + l2.val;
int carry = sum / 10;
int place = sum % 10;
if (l1.next != null && l2.next != null) {
l1.next.val += carry;
digit = AddTwoNumbers(l1.next, l2.next);
} else if (l1.next != null)
digit = AddTwoNumbers(l1.next, new ListNode(carry, null));
else if (l2.next != null)
digit = AddTwoNumbers(new ListNode(carry, null), l2.next);
else if (carry > 0)
return new ListNode(place, new ListNode(carry, digit));
return new ListNode(place, digit);
}
答:
1赞
trincot
10/6/2023
#1
递归调用不是尾部调用。尾部调用是那些递归调用,是函数执行的最后一个操作。
使用这种算法是不可能的,因为从递归调用中返回的列表不是最终结果,也不可能是。您仍然需要在返回的列表前面加上“当前”节点。
当您提到 LeetCode 的低性能等级时,您可以考虑一些改进递归实现的方法。当您更改列表时,请考虑您只需要创建一个新节点,如果由于超出最终节点的进位溢出而需要额外的节点。在所有其他情况下,可以重用给定的节点。在 的情况下,您可以只将余数从 的尾部重新连接到 的尾部,而不是用实例填充。l1
l2
l1
l2
l1
new ListNode(carry, null)
这是它的样子:
public class Solution {
public ListNode AddTwoNumbers(ListNode l1, ListNode l2) {
if (l1.next == null && l2 != null) { // Swap, so l1 is not shorter than l2
l1.next = l2.next;
l2.next = null;
}
int sum = l1.val + (l2 == null ? 0 : l2.val);
l1.val = sum % 10;
if (sum > 9 || l2 != null && l2.next != null) {
if (sum > 9 && l1.next != null) {
l1.next.val++;
}
l1.next = l1.next != null ? AddTwoNumbers(l1.next, l2 == null ? null : l2.next) : new ListNode(1);
}
return l1;
}
}
其次,在这样的平台上,运行时排名可能会波动很大,所以不要太担心。
最后,迭代解决方案肯定会表现得更好。
评论
digit
ListNode