根据在递归过程中更改的值中断多次递归

时间:2019-06-18 18:30:12

标签: javascript recursion tree

编辑:除了针对该问题的其他解决方案之外,我还热衷于了解这种递归或递归问题是否具有模式,我使用过的技术是否有名称(即通过引用基于对对象的更改中断将来的递归)此技术在其他一些情况下有用吗?

我在nAry树中寻找一个值,一旦找到它,我想破坏递归(还有其他基本情况可以破坏该示例)。代码如下所示:

function getCommentById(root, commentId, foundComment) {
  if (root.id === commentId) {
    return root;
  }

  if (foundComment.comment) {
    return foundComment.comment;
  }

  if (!root.comments) {
    return foundComment.comment;
  } else {
    for (let i = 0; i < root.comments.length; i++) {
      foundComment.comment = getCommentById(
        root.comments[i],
        commentId,
        foundComment
      );
    }
  }

  return foundComment.comment;
}

基本上,我正在浏览嵌套评论,以按其ID查找评论。

我必须遍历当前注释的所有子级,然后递归调用此函数。假设我在当前注释的child1中找到了该注释,我不想再递归而只是中断递归,但是循环将继续到下一个兄弟并递归。 这种事情在二叉树中很容易,因为我可以做类似的事情

return getCommentById(left) || getCommentById(right)

但是我在这里实现相同的逻辑时遇到了麻烦,因为我们需要以某种方式存储每个子调用的结果,并基于此决定是否找到值。因此,我的解决方案使用一个辅助变量来表示何时找到该值。我发现这应该是一个对象,而不是变量,以便在随后的child1到child2的递归调用中可以看到值的变化。如果我只是在child1递归中使用了一个标志并将其设置为true,那将是不可能的,因为那样的话,child2递归仍将标志视为false并继续递归。

有没有更好的方法? 使用对象引用破坏递归的这种技术是否有名称?还有什么可以实现的呢?

编辑:测试数据集

const post = {
id: "post1",
title: "Sample Post 1",
description: "This is a sample post",
createdBy: "user1",
createdAt: new Date(),
comments: [
  {
    id: "post1comment1",
    text: "This is comment 1",
    userId: "user1",
    timestamp: new Date().setFullYear(2018),
    comments: [
      {
        id: "post1comment1.1",
        text: "This is sub comment 1 of comment 1",
        userId: "user2",
        timestamp: new Date()
      }
    ]
  },
  {
    id: "post1comment2",
    text: "This is comment 2",
    userId: "user4",
    timestamp: new Date()
  },
  {
    id: "post1comment3",
    text: "This is comment 3",
    userId: "user4",
    timestamp: new Date()
  }
]
  },

用法:

const foundComment = { comment: null };
getCommentById(post, "post1comment1.1", foundComment);

3 个答案:

答案 0 :(得分:1)

您可以使用基于堆栈的迭代树遍历方法。这是基本概念:

function find_thing(root) {
  var stack = [ root ];
  
  while(0 < stack.length) {
     var current_thing = stack.pop();
     if(is_the_thing(current_thing)) {
       return current_thing;
     }
     
     stack = stack.concat(current_thing.AllMyKids());
  }

  return null;
}

答案 1 :(得分:1)

您可以直接将节点作为结果,而无需通过对象引用将其返回。

function getCommentById(root, commentId) {
    var temp;

    if (root.id === commentId) return root;
    (root.comments || []).some(node => temp = getCommentById(node, commentId))
    return temp;
}

const
    post = { id: "post1", title: "Sample Post 1", description: "This is a sample post", createdBy: "user1", createdAt: new Date(), comments: [{ id: "post1comment1", text: "This is comment 1", userId: "user1", timestamp: new Date().setFullYear(2018), comments: [{ id: "post1comment1.1", text: "This is sub comment 1 of comment 1", userId: "user2", timestamp: new Date() }] }, { id: "post1comment2", text: "This is comment 2", userId: "user4", timestamp: new Date() }, { id: "post1comment3", text: "This is comment 3", userId: "user4", timestamp: new Date() }] },
    result = getCommentById(post, "post1comment1.1");

console.log(result);

答案 2 :(得分:1)

添加此内容是为了完整性。 基于对我要解决的问题的更好理解,我能够将代码简化为:

function getCommentByIdSimple(root, commentId) {
  if (!root) {
    return null;
  }

  if (root.id === commentId) {
    return root;
  } else {
    let node;
    if (!root.comments) {
      return null;
    }
    for (let i = 0; i < root.comments.length; i++) {
      if ((node = getCommentByIdSimple(root.comments[i], commentId))) {
        return node;
      }
    }
  }

  return null;
}

我要解决的问题是找到所需注释后如何退出for循环。在上面的实现中,我检查是否发现node我只是返回node,所以返回后的进一步迭代将被忽略。

此方法的灵感来自: n-ary tree searching function