【发布时间】:2021-09-20 14:58:57
【问题描述】:
我正在为 Java 练习而苦苦挣扎。
我得到了一个树节点数组。每个树节点都有一个父节点和一个 int 值。我还得到一个 int,它代表一个节点值。我被要求从给定值为子树根的数组中删除子树。
我认为最好的方法可能是创建一个数组列表,并遍历输入数组,将我保留的节点添加到数组列表中,然后将该数组列表转换为数组。
我正在努力确定删除所有子树根子节点的最佳方法,因为我只有 parentId。
我走对了吗?
【问题讨论】:
我正在为 Java 练习而苦苦挣扎。
我得到了一个树节点数组。每个树节点都有一个父节点和一个 int 值。我还得到一个 int,它代表一个节点值。我被要求从给定值为子树根的数组中删除子树。
我认为最好的方法可能是创建一个数组列表,并遍历输入数组,将我保留的节点添加到数组列表中,然后将该数组列表转换为数组。
我正在努力确定删除所有子树根子节点的最佳方法,因为我只有 parentId。
我走对了吗?
【问题讨论】:
尝试使用树形图而不是数组列表。 最好的问候
【讨论】:
你有没有给树节点的根? 如果是这样,只需遍历树。一旦点击要删除的节点值,只需从要删除的节点中删除前一个节点的链接即可。
【讨论】:
此解决方案不会为您提供最佳性能,因为它会多次迭代给定数组,但它非常简单。
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;
}
}
【讨论】:
如果允许在清除 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;
}
【讨论】:
这是一个你可以使用的算法:
时间复杂度 = O(n) 空间复杂度 = O(n)
【讨论】: