【发布时间】:2021-01-18 17:54:43
【问题描述】:
我有一个树形结构{name, [children]}(JS 对象)。用户可以任意选择任何节点以部分复制结构(复制到 JS 对象数组中,因为可以选择松散叶节点)。例如用户可以选择一个叶子节点,但不能选择它的父/祖父节点,那么这个叶子节点只会在最高父节点的children 的平面数组中。
例子:
original (JS Object) selected
A✓ A
| \ \ / \
B✓ C D✓ B D
/ \ => / \
E✓ F E G
|
G✓
original selected(Array of JS objects)
A B, D
| \ \ / \
B✓ C D✓ E G
/ \ =>
E✓ F
|
G✓
我的第一个想法是:对于点击的每个节点,假设我们维护一个 selected 数组:
-
如果这个节点在
selected=> 取消选择(相当直接):a1。如果节点是“顶级”元素 => 将其从数组中删除,并将其子节点推送到数组中。
a2。如果节点是顶级元素的(大)子节点 => 从其父节点的子节点中删除该节点,并将该节点的子节点重新附加到其父节点。
-
如果这个节点不在
selected=> 选择(奇怪的事情发生):一个。用空的
children初始化tempNode,因为我们还不知道它的孩子b.如果这个节点有 (grand)children => 对于每个 (grand)child,如果 (grand)child 在
selected=> 删除selected中的 (grand)child => 将这个 (grand)child 添加到tempNode'schildren(应该递归多级)c1.如果此节点的(祖)父级已经在
selected中 => 将tempNode附加到(祖)父级(应该是多个级别的递归)c2。如果该节点的(大)父节点不在
selected中,则将其推送到数组中。
我主要停留在步骤 2b 和 2c。
//for adding node into selected
const addChildInSelected = (child) => {
var tempNode = {
name: child.name,
children: []
}
var clonedState = clone(selected)
var childInTree = findChildInTree(child.id, props.data)
//if the child has children, check if they are in selected yet.
if (childInTree.children.length > 0) {
//Problem 1: I should run this recursively for all the grandchildren,
//but I struggle to find the base case. But recursion might not be the best solution
for (var i = 0; i < childInTree.children.length; i++) {
//Problem 2: findChildInSelected/removeChildInSelect are all recursive calls as well,
//due to the tree structure. very worried about stack overflow during this step...
var grandChildInSelected = findChildInSelected(childInTree.children[i].id)
if (grandChildInSelected !== null) {
clonedState = removeChildInSelected(clonedState)
tempNode.children.push(findChildInTree(childInTree.children[i]))
}
}
}
//Problem 3. I realized I needed to check for each node in the `selected` again.
//another potential performance hurdle
}
现在我重新考虑这个问题,每个节点只关心它自己、它的直接父/祖父以及它的所有子/孙作为一个平面数组。这可能是研究这个问题的一个很好的视角。也许找到父母/祖父母或所有孩子的辅助功能将是一个好的开始。 (另外我意识到在几个函数中我忘记检查祖父母......)
但由于我使用多个树结构进行操作,所以下面的函数似乎并不理想......
//just a quick example
const findDescendantsInSelected = (node, descendants=[]) => {
//recursive call within recursive function which could overflow if the tree is big
if (findNodeInSelected(node) !== null) {
descendents.push(node)
}
if (node.children.length > 0) {
for (var i = 0; i < entry.children.length; i++) {
result = findDescendantsInSelected(node.children[i], descendants);
}
return result
}
return descendents
}
总而言之,维护几乎 3 个独立的树(选择状态、原始数据和每个步骤的克隆,因为我使用了 react)并在每个步骤中跟踪父母/孩子,跨不同的功能让我很头疼。任何有关如何简化问题的见解将不胜感激!
【问题讨论】:
-
你能用 JavaScript 对象表示法提供这些树吗?
-
您是否正在尝试找到一种算法,该算法可以在通过单击选择/取消选择时以有效的方式更新(变异)树结构,或者您是否只是在寻找一种可以返回新树的算法有选择的给定树?
-
@Bergi 正如问题中所建议的,我正在寻找在保持结构的同时复制树。这种复制可以在选择期间或之后进行。另外,由于我使用的是反应,我不能直接改变状态,我必须在每次更新时提供一个新对象......老实说,重复有点搞砸了我。尽管我将变量命名为
parentInOriginal和parentInSelected,但我仍然在某些时候感到非常困惑...... -
@trincot 绝对!我将嵌套的 obj 用于树,例如
{ name: A, children: [{name: B, children: []}]。虽然我可以切换到使用name作为键,例如[{name: A, children:[B]},{name: B}]。最后,我会接受高级伪代码甚至只是一个冗长的解释作为答案哈哈。
标签: javascript tree