【问题标题】:How does recursion works in printing the linked list elements in reverse order?递归如何以相反的顺序打印链表元素?
【发布时间】:2017-05-16 13:54:12
【问题描述】:

我对递归的工作原理有了一点了解。堆栈提供的变量的调用和不同的上下文,用于存储先前调用的函数返回的值。

例如,

void Factorial(int n)
{
  if(n==0) return 0;
  else if(n==1) return 1;
  else
  {
    return n*Factorial(n-1);
  }
}

在这个用于计算给定数字的阶乘的递归函数中,我知道假设给定 n 为 5 的第一次调用将是 5*Factorial(4*Factorial(3....的基本条件))然后它会按照总结值的顺序返回,例如 1*2*3*4*5=240。

我不明白的是这段代码以相反的顺序打印单链表的项目。

void ReversePrint(Node head) {    
if(head==null)
    {
    return;
    }
else
    {
    ReversePrint(head.next);
    System.out.println(head.data);
    }
}

据我所知,链表函数的元素数量是有限的,

首先, 由于 head 不为 null,它将进入 else 条件,并且 head 将等于 head.next 直到它变为 null。然后呢?

我可能无法准确地表达我的问题。希望你能解释一下。提前致谢。

【问题讨论】:

  • 我认为该问题至少有 543 个重复项...
  • 是的,似乎没有人回答我的问题。如果是这样,何必再问?

标签: java recursion singly-linked-list


【解决方案1】:

添加一些额外的 cmets 应该会有所帮助:

void ReversePrint(Node node) {
    if (node == null) {
        // An empty list has nothing to print.
        return;
    } else {
        // First print the rest of the list.
        ReversePrint(node.next);
        // Then print this node.
        System.out.println(node.data);
    }
}

所以 - 本质上 -

  • 空列表不打印任何内容。
  • 非空列表应首先打印列表的其余部分,然后打印根节点。

导致列表向后打印。

【讨论】:

  • "一个非空列表应该先打印列表的其余部分,然后打印根节点"- 它是如何反向打印列表的其余部分的?
  • @KammariNagendra if (node == null) 部分处理空列表。 else 部分通过首先打印 node.next 然后打印 root 节点来处理非空。这样,代码将递归到列表的末尾,然后在递归展开时打印每个节点(反向)。
【解决方案2】:

这个问题可能有很多重复项,但我不想找到它们并将它们链接起来,而是想解决一些与您所问问题有关的问题。

对于阶乘示例,递归效果很好,因为通常您不希望计算非常高的阶乘,并且如果您确实需要,通常会在遇到堆栈溢出之前遇到整数溢出。

但是,列表的大小通常大于 1000 个元素。对于我的实现,ReversePrint 方法给了我一个大于大约 10000 个元素的 StackOverflowError 链接列表,这是由于 Java 限制了内存中的堆栈大小。

对于这样的应用程序,最好使用以下方法以非递归方式执行它:

ReversePrint(YourList l) {
    Node n = l.last;
    while (n != null) {
        System.out.println(n.data);
        n = n.prev;
    }
}

请注意,完成这样的事情需要一些额外的功能。列表必须是双向链接的,包括向前和向后的,并且必须有某种 YourList 类来封装它(为简单起见)。

【讨论】:

  • 是的,感谢您的努力,但我的问题仅限于单链表。
  • 问题不在于递归的实用性,也不在于您对问题大小的未经证实的主张,甚至不在于您使用没有证据的字段的解决方案;它是关于理解递归如何/为什么在适当的时候起作用。
  • @ScottHunter 是的,我确实意识到我的回答并不能完全回答这个问题。但是,由于 Stack Overflow 上有很多重复的问题,描述了头和尾递归之间的差异。虽然其他 cmets/answers 指出了这一点,但我试图将注意力集中在使用递归解决诸如以相反顺序打印列表等特定问题的问题方面。
【解决方案3】:

假设您有一个名为ReversePrintRest 的函数,它接受一个列表并打印所有内容反向打印。然后你可以很容易地写ReversePrint

void ReversePrint(Node head) {    
    if(head==null) {
        return;
    } else {
        ReversePrintRest(head);
        System.out.println(head.data);
    }
}

只要ReversePrintRest 像宣传的那样工作,我假设你同意这个工作没有问题。

ReversePrintRest的写法也很简单:

void ReversePrintRest( Node head ) {
    ReversePrint( head.next );
}

再次假设ReversePrint 有效,其正确性也很容易看出。而且由于ReversePrintRest 的正文直接取自您的原始代码,因此您的原始代码也应该是正确的。

这可能看起来有点花招:每个函数的正确性取决于另一个函数的正确性。但我们可以更明确一点ReversePrint

  • 它显然适用于空列表
  • 假设它适用于较短的列表,它适用于当前的列表,它依赖于已知的适用于较短列表的代码并使用它做正确的事情(即,在反向打印其余部分后打印头部)。

只剩下假设来证明:

  • 我们知道它适用于空列表
  • 因为它适用于空列表,所以它适用于长度为 1 的列表。
  • 由于它适用于长度为 1 的列表,因此也适用于长度为 2 的列表。

只要你需要任何长度的 N 列表,你就可以遵循这个逻辑。由于该参数不依赖于 N 的特定值,因此它适用于所有这些值。

【讨论】:

  • “想象一下你有一个名为 ReversePrintRest 的函数,它接受一个列表并打印除头部之外的所有内容”- 我不明白它在哪里打印? void ReversePrintRest( Node head ) { ReversePrint( head.next ); } 显然我在这个函数中看不到打印语句?
  • 它调用ReversePrint;进去看看。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-25
  • 1970-01-01
  • 1970-01-01
  • 2016-02-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多