如何使用递归获得父母的所有孩子,然后是他们的孩子

时间:2010-02-16 19:51:21

标签: java jsp servlets recursion

问候:

我的JSP Web应用程序中有父事务的比喻。我将事务ID存储在数据库中,并且要求显示父项的所有子项,然后显示父项子项的后续子项。实际上,这个父母及其子女的名单将永远不会超过4或5级,但我需要考虑到它可以比这更多层次。

我试过这样做会递归如下:

private static void processChildrenTransactions(
    AllTremorTransactionsVO parentBean,
    ArrayList<AllTremorTransactionsVO> childCandidatesList )
{
  ArrayList<AllTremorTransactionsVO> childList =
      new ArrayList<AllTremorTransactionsVO>();

  for (AllTremorTransactionsVO childTransactions : childCandidatesList)
  {
    if (childTransactions.getParentGuid() != null)
    {
      if (childTransactions.getParentGuid().equals(parentBean.getTransactionGuid()))
      {
        childList.add(childTransactions);
      }
    }
  }

  for (AllTremorTransactionsVO allTremorTransactionsVO : childList)
  {
    processChildrenTransactions(allTremorTransactionsVO, childList);    
  }

  return;
}

这不起作用,在循环运行时生成堆栈溢出。关于如何做到这一点的任何想法?

5 个答案:

答案 0 :(得分:8)

如果方法的参数不能立即解决,则存在深度递归(存在使堆栈爆炸的风险)的方法。即被调用方法的最终结果取决于方法本身的结果。伪:

Result process(Parent parent) {
    Result result = new Result();
    for (Child child : parent.getChildren()) {
        result.update(process(child));
    }
    return result;
}

这会导致代码等待update(),直到结果已知,因此它会保留在堆栈中。它会随着每个方法调用而累积。

您可以优化它以使用tail recursion而不是使用可变结果对象作为参数:

void process(Parent parent, Result result) {
    for (Child child : parent.getChildren()) {
        result.update(child);
        process(child, result);
    }
}

这样,update()可以立即执行,因为参数可以立即解决。只要在调用process()之后没有返回值或任何其他逻辑发生,运行时就可以通过从堆栈中删除调用来优化它。另请参阅前面提到的有关尾递归的wiki文章和this website

但是..你发布的代码似乎已经是尾递归的。所以问题出在其他地方。在研究了你的代码后,你似乎每次都在迭代相同的孩子。即这只是无限循环的手段。可能if检查是伪造的和/或孩子们在自己的父子树中有反向引用。

答案 1 :(得分:2)

我猜你有一个无限循环...

  1. childList拥有相同的父级
  2. 根据定义,
  3. allTremorTransactionsVOchildList
  4. 的元素之一
  5. - &GT;当您递归时,您将再次构建相同的childList并选择之前的第一个allTremorTransactionsVO
  6. 我通常会像这样构建这样的递归代码:

    public List allChildren( Transaction trans )
    {
       List allChildren = new List();
       for( Transaction childTrans : trans.getChildren() )
       {
           allChildren.addAll( allChildren( childTrans ));
       }
       return allChildren;
    }
    

答案 2 :(得分:1)

我最近遇到了同样的问题(使用了太多的堆栈)并使用了迭代算法。该示例使用Javascript,但可以很容易地进行调整:

var nodeStack = new Array();

nodeStack.push(root);

while(nodeStack.length > 0) {
   var currentNode = nodeStack.pop();
   var data = currentNode.data;
   var children = currentNode.children;

   /* do stuff with data */

   if(children.length > 0) {
       jQuery.each(children, function(index, value) {
       nodeStack.push(value);
   });
}

答案 3 :(得分:0)

如果发生堆栈溢出,则递归运行得太深。您必须将算法重写为迭代(即使用for / while / foreach循环)构造。

答案 4 :(得分:0)

我能够按如下方式解决我的StackOverflow条件:

private static void processChildrenTransactions(AllTremorTransactionsVO parentBean, ArrayList<AllTremorTransactionsVO> childCandidatesList ) {
    ArrayList<AllTremorTransactionsVO> childList = new ArrayList<AllTremorTransactionsVO>();
    ArrayList<AllTremorTransactionsVO> childListTwo = new ArrayList<AllTremorTransactionsVO>();

    for (AllTremorTransactionsVO childTransactions : childCandidatesList) {
        childListTwo.add(childTransactions);
        if (childTransactions.getParentGuid() != null) {
            //gets a list of every trans with a child
            if (childTransactions.getParentGuid().equalsIgnoreCase(parentBean.getTransactionGuid())){
                childList.add(childTransactions);
                childListTwo.remove(childTransactions);
            }
        }
    }

    parentBean.setChildTransactions(childList);

    for (AllTremorTransactionsVO allTremorTransactionsVO : childList) {
        processChildrenTransactions(allTremorTransactionsVO, childListTwo);
        //processChildrenTransactions(allTremorTransactionsVO, childList);
    }

    return;

}

我现在拥有所有的孩子,但我正在努力将他们正确地分组。它们需要像这样列出:

父 --Child /父 - - 儿童 亲 --Child / Parnet ----父/子 ------儿童

等等。