在链表的这个递归反转器中混淆返回变量/值

时间:2015-09-26 21:15:25

标签: java recursion linked-list

前几天我问了一个类似的问题,但是当谈到这个递归链表反向方法如何工作的具体细节时,并没有真正得到我正在寻找的答案。

我有以下方法:

CREATE PROCEDURE intervals_generator (IN start_date DATE, IN intervals TEXT)
BEGIN
    DECLARE new_start DATE;

    CASE intervals
        WHEN '1_day' THEN
            -- Nothing to do, DATE has already truncated the time portion.
            SET new_start := start_date;
        WHEN '1_month' THEN
            -- Set to the year and month of the start date
            SET new_start := MAKEDATE(YEAR(start_date), 1) + INTERVAL (MONTH(start_date) - 1) MONTH;
        WHEN '1_quarter' THEN
        BEGIN
            -- Set to the year and month of the start date
            SET new_start := MAKEDATE(YEAR(start_date), 1) + INTERVAL (MONTH(start_date) - 1) MONTH;
            -- Subtract the necessary months for the beginning of the quarter
            SET new_start := new_start - INTERVAL (MONTH(new_start) - 1) % 3 MONTH;
        END;
        WHEN '1_year' THEN
            -- Set the date to the first day of the year
            SET new_start := MAKEDATE(YEAR(start_date), 1);
    END CASE;

    SELECT new_start;
END//

如果我在递归调用后修改public Node reverse(Node current) { if(current == null || current.next == null) { return current; } Node rest = reverse(current.next); current.next.next = current; current.next = null; return rest; } 变量,但仍返回current,那么rest如何修改/附加列表中的下一个链接?当我跟踪这个函数时,似乎rest被分解为它的最后一个元素,然后只返回最后一个元素。

我没有看到在递归调用之后修改rest变量对返回值有什么影响。

我已多次追踪此功能,这是一个例子:

让我们说currentNode in = 1

跟踪似乎如下:

in.next = 2

从现在开始,

reverse(in) (in is currently 1 , 2)现在被称为in

current通过基本案例返回2

现在rest = reverse(current.next) which is equivalent to: rest = reverse(2)

current.next.next =当前

使rest = 2 ???

current = 1 , 2 , 1 , 2设置current = 1

函数返回current.next = null没有rest which is equal to 2值,我可以看到它在任何地方都没有被修改过,但是当我测试这个.next

的输出时

那么如何正确交换休息呢?

我觉得好像在我没有捕获的变量之间存在某种引用,因为没有rest is 2 , 1的声明

有人会介意为我解决这个问题吗?

看起来很简单,但直到现在这一直是我的头脑。但是,此功能正常工作。看起来我的痕迹似乎无法告诉我如何。

3 个答案:

答案 0 :(得分:2)

您返回递归调用返回的任何内容,未更改。那个递归调用返回了什么?链表中的最后一个节点。

很自然地,最后一个节点在反向列表中成为第一个。

保存返回值后的最后几行处理将当前节点链接到反向列表的当前最后节点,此时是 next

重新链接完成后,您可以自由地返回现在完全颠倒列表的头部:

curr ->   next  ->  ....    -> last 
curr ->   next  ->  ... <- ... last
curr ->   <- next   ... <- ... last
. <- curr <- next   ... <- ... last

每次调用递归函数都会在单独的堆栈框架上维护它自己的函数内部变量集。例如:

reverse( {101= val:1 next:102}, {102= val:2 next:103}, {103= val:3 next:null} )

                    => reverse( {102= val:2 next:103}, {103= val:3 next:null} )

                                          =>  reverse( {103= val:3 next:null} )
                                          |            curr                    curr.next == null
                                         <= {103}
                    |- - - - - - - - - - - - - - - - - - - - - - - - - - - -   curr == 102
                    |           curr                   curr.next               curr.next == 103
                    |                                  {103= val:3 next:102}   curr.next.next = curr
                    |           {102= val:2 next:null}                         curr.next = null
                   <= {103}
   |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  curr == 101
   |     curr                    curr.next                                     curr.next == 102
   |                             {102= val:2 next:101}                         curr.next.next = curr
   |     {101= val:1 next:null}                                                curr.next = null
  <= {103}           

current.next = null代码行仅在最后一次调用时需要,该调用与原始列表的第一个节点一起使用。它可以在所有其他调用中跳过,但这需要更多代码来安排,并且会使临时列表处于非相干状态。

答案 1 :(得分:2)

为了获取您的示例案例,让我们说我们的列表包含2个项目, 1 &amp; 2 1 在哪里,他们在开始时有这些值:

  

<强> 1
  值= 1
  下一个= 2

     

<强> 2
  值= 2
  下一个= null

您调用reverse(1)并在该上下文中(让我们通过递归深度和嵌套标记它):

  

<强> R1
  电流= 1
  的 R2
  电流= 2
  (返回2)
  休息= 2
  当前(1) .next (2) .next (赋值前为null) =当前(1) |所以现在2.next == 1
  当前(1) .next (2) = null |所以现在1.next == null
  (返回Rest (即2)

现在,如果在1之前还有其他项目,那么该项目将被处理后,它会将 1 next 设置为自身,就像在上面的示例, 1 2 next 设置为自身。

答案 2 :(得分:1)

这是有效的,因为变量是对象的引用,而不是当前对象。

举个例子(这不是为了遵守任何程序语言语法):

objects = {e1, e2, e3};
e1.next = e2; e2.next = e3; e3.next = null;

此时,e1.nexte2.next是对内存中存在的正确对象的接受,而e3.nextnull,在这种情况下不会引用任何对象。 / p>

而不是x是对y的引用,我会说xy,以使解释更紧凑和简单。无论如何,变量是指向对象的指针。我还使用了2个空格来缩进,所以这些线条不会太长。

然后你打电话给reverse(e1),让我们打开递归,解释每一行:

reverse(e1); // current points e1
{
  if(current == null || current.next == null) // false
  {
    return current; // not executed
  }
  Node rest = reverse(current.next); // recursion, here current points e2
  {
    if(current == null || current.next == null) // false
    {
      return current; // not executed
    }
    Node rest = reverse(current.next); // recursion, here current points e3
    {
      if(current == null || current.next == null) // true
      {
          return current; // returns current that points e3
      }
      current.next.next = current; // not executed
      current.next = null; // not executed
      return rest; // not executed
    } // end of recursion
    // now rest points e3 (the "if" above was true, returning current)
    // current is e2 again
    current.next.next = current; // e2.next.next (this is e3.next) = e2
    current.next = null; // e2.next points nothing
    return rest; // return e3 (rest points e3)
  } // end of recursion
  // now rest points e3
  // current points e1 again
  current.next.next = current; // e1.next.next (this is e2.next) = e1
  current.next = null; // e1.next points nothing
  return rest; // return a pointer to e3
}

因此,您获得了e3e3.next = e2以及e2.next = e1e1.next = null

请注意,递归的数量是列表中的对象数减1,因此如果列表有1000个元素,则堆栈将填充1000个调用(一个正常的程序调用和999个过度递归)。对于内存较小的小型设备,这可能是一个大问题(可能是20个元素是一个问题),但我从来没有必要在PC上讨论堆栈大小。