从树节点数组中删除子树

问题描述

我正在努力进行 Java 练习。

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

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

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

我在正确的轨道上吗?

解决方法

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

,

你得到树节点的根了吗? 如果是这样,只需遍历树。一旦你命中了要删除的节点值,只需从要删除的节点中删除上一个节点的链接即可。

此链接解释了它-Remove Subtree with specific value

,

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

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,5),new Node(33,22,3),new Node(44,4),new Node(66,55,6),new Node(77,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;
    }

}
,

如果允许您在清除 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,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,n)));

输出:

[(1 : *),(2 : 1),(3 : 2),(4 : 3),(2 : *),(5 : 4),(5 : *),(6 : 5),(7 : 6) ]
[(1 : *),(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;
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...