【问题标题】:How to iterate through a list of nodes which might have sub-lists of nodes (unknown depth levels)如何遍历可能具有节点子列表(未知深度级别)的节点列表
【发布时间】:2022-09-15 22:55:15
【问题描述】:

我有一个节点列表,每个节点可能有一个子节点列表(级别数未知):

class Node {
    int score;
    boolean selected;
    List<Node> subNodes;
}

下面是一个假设结构的样子:

NODE
  + NODE
      + NODE
      + NODE
          + NODE
      + NODE
  + NODE
      + NODE
          + NODE
          + NODE
              + NODE
              + NODE

组合数不胜数。我需要一种方法来对所有将NODE.selected 设置为true 的节点求和NODE.score,可能使用Java 8 功能。任何提示将不胜感激。

  • 听起来像是一个不错的递归用例。

标签: java list java-stream tree-traversal


【解决方案1】:

就像是:

public int recursiveTotal(final Node node) {
    //node not select, don't count the node or any of its subnodes
    if (!node.selected) {
        return 0;
    }
    //no subnodes, only node score counts
    if (node.subNodes.isEmpty()) {
        return node.score;
    }
    //node has subnodes, recursively count subnode score + parent node score
    int totalScore = node.score;
    for (final Node subNode : node.subNodes) {
        totalScore += recursiveTotal(subNode);
    }
    return totalScore;
}

使用 stackoverflow 作为 IDE 进行编码,不能保证不会出现编译错误;)

【讨论】:

    【解决方案2】:

    在 Node 类中创建一个递归方法,该方法返回连接父节点和子节点流的节点流:

    class Node {
        int score;
        boolean selected;
        List<Node> subNodes;
    
        public Stream<Node> streamNodes() {
            return Stream.concat(Stream.of(this), subNodes.stream().flatMap(Node::streamNodes));
        }
    }
    

    并像下面一样使用它来流式传输您的列表:

    List<Node> myNodes = //your list
    int sum = myNodes.stream()
                     .flatMap(Node::streamNodes)
                     .filter(Node::isSelected)
                     .mapToInt(Node::getScore)
                     .sum();
    

    【讨论】:

      【解决方案3】:

      TL;博士

      从结构来看,您在List 中提供的每个Node一个N-ary 树数据结构(我假设没有圆圈)。

      为了获得所需的数据,我们可以使用经典的tree-traversal algorithms 之一。平均深度小于平均宽度时深度优先搜索算法会更合适,因为它会更节省空间,在相反的情况下使用会更好广度优先搜索.我会选择 DFS。

      提出递归实现更容易,所以我将从它开始。但它有Java 中的实用价值,因此我们将继续进行一些改进。

      流+递归

      您可以创建一个辅助方法来负责展平将从流中调用的节点。

      List<Node> nodes = // initializing the list
              
      long totalScore = nodes.stream()
          .flatMap(node -> flatten(node).stream())
          .filter(Node::isSelected)
          .mapToLong(Node::getScore)
          .sum();
      

      递归辅助方法:

      public static List<Node> flatten(Node node) {
          if (node.getSubNodes().isEmpty()) {
              return List.of(node);
          }
          
          List<Node> result = new ArrayList<>();
          result.add(node);
          node.getSubNodes().forEach(n -> result.addAll(flatten(n)));
          return result;
      }
      

      没有递归

      避免StackOverflowError方法flatten()可以实现没有通过轮询和分配新的递归节点在堆栈上(由ArrayDeque 表示)迭代。

      public static List<Node> flatten(Node node) {
          
          List<Node> result = new ArrayList<>();
          Deque<Node> stack = new ArrayDeque<>();
          stack.add(node);
          
          while (!stack.isEmpty()) {
              Node current = stack.poll();
              result.add(current);
      
              current.getSubNodes().forEach(stack::push);
          }
          return result;
      }
      

      无递归和无中间数据分配

      以以下形式分配中间数据节点最终不会被使用是不切实际的。

      相反,我们可以让辅助方法负责计算总得分通过汇总每个分数产生选定节点在里面节点树.

      为此,我们需要在遍历树时执行isSelected()

      List<Node> nodes = // initializing the list
              
      long totalScore = nodes.stream()
          .mapToLong(node -> getScore(node))
          .sum();
      
      public static long getScore(Node node) {
          long total = 0;
          
          Deque<Node> stack = new ArrayDeque<>();
          stack.push(node);
          
          while (!stack.isEmpty()) {
              Node current = stack.poll();
              if (current.isSelected()) total += current.getScore();
      
              current.getSubNodes().forEach(stack::push);
          }
          return total;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-06-25
        • 2014-04-20
        • 1970-01-01
        • 2013-07-20
        • 1970-01-01
        • 1970-01-01
        • 2021-01-27
        相关资源
        最近更新 更多