【问题标题】:Delete subtree from an array of treenodes从树节点数组中删除子树
【发布时间】:2021-09-20 14:58:57
【问题描述】:

我正在为 Java 练习而苦苦挣扎。

我得到了一个树节点数组。每个树节点都有一个父节点和一个 int 值。我还得到一个 int,它代表一个节点值。我被要求从给定值为子树根的数组中删除子树。

我认为最好的方法可能是创建一个数组列表,并遍历输入数组,将我保留的节点添加到数组列表中,然后将该数组列表转换为数组。

我正在努力确定删除所有子树根子节点的最佳方法,因为我只有 parentId。

我走对了吗?

【问题讨论】:

    标签: java arrays subtree


    【解决方案1】:

    尝试使用树形图而不是数组列表。 最好的问候

    【讨论】:

    • 谢谢。我会调查的。
    【解决方案2】:

    你有没有给树节点的根? 如果是这样,只需遍历树。一旦点击要删除的节点值,只需从要删除的节点中删除前一个节点的链接即可。

    这个链接解释它-Remove Subtree with specific value

    【讨论】:

    • 感谢您的链接。我得到要删除的子树的根,但没有树的根。我只是给了一个节点数组,其中每个节点都有一个 parentid 和一个值,以及要删除的子树的根节点的值。
    【解决方案3】:

    此解决方案不会为您提供最佳性能,因为它会多次迭代给定数组,但它非常简单。

    public class MavenMain {
    
        public static final class Node {
            private final int id;
            private final Integer parentId;
            private final int val;
    
            public Node(int id, Integer parentId, int val) {
                this.id = id;
                this.parentId = parentId;
                this.val = val;
            }
    
        }
    
        public static void main(String... args) {
            /*
             *          1
             *         / \
             *       2     5
             *      / \   / \
             *     3   4 6   7
             */
            Node[] nodes = {
                    new Node(11, null, 1),
                    new Node(22, 11, 2), new Node(55, 11, 5),
                    new Node(33, 22, 3), new Node(44, 22, 4), new Node(66, 55, 6), new Node(77, 55, 7) };
            Node[] res = removeSubtree(nodes, 1);  // remove root is 2
        }
    
        public static Node[] removeSubtree(Node[] nodes, int subTreeRoot) {
            Node removeRoot = nodes[subTreeRoot];
    
            if (removeRoot.parentId == null) // this is a root, i.e. remove whole tree
                return new Node[0];
    
            Set<Integer> removedNodes = new HashSet<>();
            removedNodes.add(removeRoot.id);
    
            while (true) {
                boolean found = false;
    
                for (int i = 0; i < nodes.length; i++) {
                    if (removedNodes.contains(nodes[i].id) || !removedNodes.contains(nodes[i].parentId))
                        continue;
    
                    removedNodes.add(nodes[i].id);
                    found = true;
                }
    
                if (!found)
                    break;
            }
    
            Node[] res = new Node[nodes.length - removedNodes.size()];
    
            for (int i = 0, j = 0; i < nodes.length; i++)
                if (!removedNodes.contains(nodes[i].id))
                    res[j++] = nodes[i];
    
            return res;
        }
    
    }
    

    【讨论】:

      【解决方案4】:

      如果允许在清除 Nodes 之前更新它们,一个简单的方法是遍历数组并使用递归函数跟踪从每个节点到数据与查询数据匹配的根节点或父节点的路径.在后一种情况下,我们将路径上所有节点的数据更新为查询值。然后,在第二次遍历数组时,我们对其进行压缩,删除值与查询匹配的节点。

      这里有一些 Java 代码来说明:

      static int clearPath(Node[] arr, int query)
      {
          for(int i=0; i<arr.length; i++)
              if(arr[i].data != query)
                  markPath(arr[i], query);
          
          int j = 0;
          for(int i=0; i<arr.length; i++)
              if(arr[i].data != query)
                  arr[j++] = arr[i];
          
          return j;
      }
      
      static boolean markPath(Node node, int query)
      {
          if(node == null) return false;
          
          if(node.data == query) return true;
          
          boolean located = markPath(node.parent, query);
          if(located) node.data = query;
          return located;
      }
      

      还有Node 类:

      static class Node
      {
          Node parent;
          int data;
          public Node(Node parent, int data)
          {
              this.parent = parent;
              this.data = data;
          }
          
          public String toString()
          {
              return String.format("(%d : %s) ", data, (parent == null ? "*" : parent.data)); 
          }
      }
      

      测试:

      int[][] tree = {{1, 2, 3, 4}, {2, 3, 4, 5}, {5, 6, 7}};
      List<Node> nodeList = new ArrayList<>();
      for(int[] sub : tree)
      {
          Node node = null;
          for(int data : sub)
              nodeList.add(node = new Node(node, data));              
      }    
      Node[] arr = nodeList.toArray(new Node[nodeList.size()]);
      
      System.out.println(Arrays.toString(arr));
      
      int n = clearPath(arr, 3);
      
      System.out.println(Arrays.toString(Arrays.copyOfRange(arr, 0, n)));
      

      输出:

      [(1 : *) , (2 : 1) , (3 : 2) , (4 : 3) , (2 : *) , (3 : 2) , (4 : 3) , (5 : 4) , (5 : *) , (6 : 5) , (7 : 6) ]
      [(1 : *) , (2 : 1) , (2 : *) , (5 : *) , (6 : 5) , (7 : 6) ]
      

      如果您不允许更新节点数据,您可以使用 Set 来保存要删除的节点。

      static int clearPath2(Node[] arr, int query)
      {
          Set<Node> rem = new HashSet<>();        
          for(int i=0; i<arr.length; i++)
              markPath2(rem, arr[i], query);
      
          int j = 0;
          for(int i=0; i<arr.length; i++)
              if(!rem.contains(arr[i])) arr[j++] = arr[i];
          
          return j;
      }
      
      static boolean markPath2(Set<Node> rem, Node node, int query)
      {
          if(node == null) return false;
          
          if(node.data == query || markPath2(rem, node.parent, query)) 
          {
              rem.add(node);
              return true;
          }
          return false;
      }
      

      【讨论】:

      • 这很有帮助。谢谢!
      【解决方案5】:

      这是一个你可以使用的算法:

      1. 在第一轮中,从 {parentIdx -> [childIdx1, childIdx2]} 创建一个映射 M1,从 {value -> idx} 创建一个映射 M2
      2. 给定需要删除的子树根的值,使用 M2 找到它的 idx。这是您可以在 M1 中使用的 parentIdx
      3. 在 M1 中执行深度优先搜索,从您在步骤 (2) 中找到的 parentIdx 开始。保留一个访问过的集合,记录您访问过的所有子索引。
      4. 现在,遍历原始数组并删除您在步骤 (4) 中访问的索引
      5. 你是金子!

      时间复杂度 = O(n) 空间复杂度 = O(n)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-04
        • 1970-01-01
        • 1970-01-01
        • 2017-07-03
        相关资源
        最近更新 更多