Java中的链接列表递归

时间:2014-06-23 02:19:59

标签: java recursion

我必须编写一个递归方法,该方法遍历链表并返回正数的整数。这是一个问题:

下面的方法countPos必须是一个带Node头的递归方法 作为其参数,沿着head为首的列表,并计算具有正数据字段的节点数。

我的代码有效,但我不明白它是如何工作的。

public int countPos(Node head) {
    int count = 0; 
    if (head == null) { return count; }

    if (head.data > 0) {
        count++; 
        return count + countPos(head.next);
    } else {
        return count + countPos(head.next);
    }
}

我遇到的问题是,我不知道每次调用该方法时计数如何都不会被设置为0。由于某种原因,下次调用该方法时将忽略语句int count = 0;。这是因为我还要回归count吗?任何解释将不胜感激。

感谢。

2 个答案:

答案 0 :(得分:3)

不要通过跟踪执行或调试来开始。递归的强大之处在于它可以让你用简单的逻辑来推理复杂的程序。

您的代码是偶然的。它反映了谁写了它(你呢?)并不了解递归如何解决问题。它比必要的复杂得多。

要利用递归,请考虑手头的问题:

  1. 定义功能界面。
  2. 将问题分成几部分,其中至少有一部分是同一问题的较小版本。
  3. 通过调用函数接口本身解决那些(或那些)较小的版本。
  4. 找到"基本情况"或者解决同一问题的非常小的实例的案例。
  5. 完成所有这些后,大多数递归算法的伪代码是:

    function foo(args)
    
       if args describe a base case
         return the base case answer.
    
       solve the smaller problem or problems by calling foo with 
         args that describe the smaller problem!
    
       use the smaller problem solution(s) to get the answer for this set of args
    
       return that answer
    end
    

    让我们将此应用于您的案例:

    问题:计算列表中的正数项目。

    1. 定义功能界面:int countPos(Node head)
    2. 将问题分解为多个部分:在头部之后获取剩余列表中的肯定数量,然后在头部为正数时添加一个,如果头部为零或负数则不添加任何一个。“ LI>
    3. 问题的较小版本是查找列表中已删除头部的肯定数:countPos(head.next)
    4. 查找基本案例:空列表为零肯定。
    5. 把这一切放在一起:

      int countPos(Node head) {
      
        // Take care of the base case first.
        if (head == null) return 0;
      
        // Solve the smaller problem.
        int positiveCountWithoutHead = countPos(head.next);
      
        // Now the logic in step 2. Return either the positive count or 1+ the positive count:
        return head.data > 0 ? positiveCountWithoutHead + 1 : positiveCountWithoutHead;
      }
      

      可能通过跟踪这样的事情的执行来学习一点点。但是,试图通过推理堆栈的内容来编写递归代码是一个死胡同。要取得成功,你必须在更高层次上思考。

      让我们尝试一下并不完全遵循标准模板:递归二进制搜索。我们有一个整数数组a,并且如果数组中存在x,则会尝试查找-1的索引,如果不存在则返回i0

      问题:在位置i1-1i0之间搜索数组。

      (上面是一个例子,说明你有时必须通过添加参数来解决问题,以便在递归调用或调用中描述较小的子问题。这里我们添加新的参数{{ 1}}和i1以便我们可以指定a的子数组。了解如何以及何时执行此操作是一个实践问题。所需的参数可能因语言特征而异。)

      1. 功能界面:int search(int [] a, int x, int i0, int i1)
      2. 将问题分成几部分:我们选择一个"中间"元素索引:mid = (i0 + i1) / 2。然后子问题要么搜索数组的前半部分,要么排除mid,或者在 mid之后从开始并继续到结尾的数组的后半部分。
      3. 来电是search(a, x, i0, mid)search(a, x, mid + 1, i1)
      4. 基本情况是1)如果i0 >= i1,则没有要搜索的元素,所以如果我们-1,则返回a[mid] == x和2),然后我们就找到了x并且可以返回mid
      5. 把这一切放在一起

        int search(int [] a, int x, int i0, int i1) {
        
          // Take care of one base case.
          if (i0 >= i1) return -1;
        
          // Set up mid and take care of the other base case.
          int mid = (i0 + i1) / 2;
          if (a[mid] == x) return mid;
        
          // Solve one or the other subproblems.  They're both smaller!
          return x < a[mid] ? search(a, x, i0, mid) : search(a, x, mid + 1, i1);
        }
        

        开始搜索:

        int search(int [] a, int x) { return search(a, x, 0, a.length); }
        

答案 1 :(得分:2)

每次拨打countPos()时,都会启动该功能的新版本。这个函数从一个干净的平板开始,意味着所有的局部变量(count)都是它自己的,而没有其他的#34; copy&#34; countPos可以查看或修改其局部变量。

这些&#34;副本之间传递的唯一状态&#34;或countPos是作为参数传递的变量(Node head)。

因此,假设列表[1,-2,3]

,这是一个粗略的工作流程
  1. countPos开始,并说正节点的数量等于1,因为&#34; 1&#34;是积极的。无论下一个函数返回什么,正节点的总数等于1 +。
  2. 下一个函数表示正节点的数量等于0 +,无论下一个函数返回什么。
  3. 下一个函数表示正节点的数量等于1 +,无论下一个函数返回
  4. 下一个函数看到head == null,因此返回0.
  5. 现在每个递归函数一个接一个地返回到调用它的原始函数,其中包含正节点的总数&#34;滚雪球&#34;当我们回来时。

    最后返回的总数将是2。