我有一个函数可以通过“节点”,这些节点具有一个键和自己的nodes
数组。我也有一组允许的键。目标很简单:如果节点具有允许的密钥,或者子节点具有允许的密钥,则保留该密钥,否则,将其删除。当前的解决方案如下所示:
var allowedKeys = ['x', 'y', 'z'];
var nodes = [{
key: 'x',
nodes: []
},
{
key: 'b',
nodes: [{
key: 'y',
nodes: []
},
{
key: 'lol',
nodes: []
},
]
},
{
key: 'c'
},
{
key: 'd',
nodes: []
},
{
key: 'e',
nodes: [{
key: 't',
nodes: [{
key: 'z',
nodes: []
}]
},
{
key: 'r',
nodes: []
},
]
},
{
key: 'f',
nodes: []
}
];
function hasChildNodes(node) {
var hasChildNodes = node.hasOwnProperty('nodes') && node.nodes.length > 0;
return hasChildNodes;
}
function removeUnnecessaryNodes(nodes, allowedKeys) {
nodes.forEach(node => {
if (hasChildNodes(node)) {
node.nodes = removeUnnecessaryNodes(node.nodes, allowedKeys);
}
});
nodes = nodes.filter(node => allowedKeys.includes(node.key) || hasChildNodes(node));
return nodes;
}
var filteredNodes = removeUnnecessaryNodes(nodes, allowedKeys);
console.log(filteredNodes);
我想将其重构为使用尾递归,以避免炸毁堆栈。我开始觉得这是不可能的。
P.S。我意识到,大多数JavaScript实现都不使用尾递归优化,这很有趣。
答案 0 :(得分:1)
在我的评论之后,我想尝试一个堆栈版本。它不像我希望的那样干净,但是我认为无论如何我都会张贴它,以希望它能以某种方式发光。
var nodes = [{
key: 'x',
nodes: []
},
{
key: 'b',
nodes: [{
key: 'y',
nodes: []
},
{
key: 'lol',
nodes: []
},
]
},
{
key: 'c'
},
{
key: 'd',
nodes: []
},
{
key: 'e',
nodes: [{
key: 't',
nodes: [{
key: 'z',
nodes: []
}]
},
{
key: 'r',
nodes: []
},
]
},
{
key: 'f',
nodes: []
}
];
var allowedKeys = ['x', 'y', 'z'];
const removeUnnecessaryNodes = (nodes, allowedKeys) => {
const allowed = new Set(allowedKeys);
const root = {nodes: nodes};
const stack = [root];
while (stack.length) {
let curr = stack.pop();
if (curr.nodes && curr.nodes.length) {
// non-empty node, push children on stack
curr.nodes.forEach(e => {
e.parent = curr;
stack.push(e);
});
}
else if (!allowed.has(curr.key)) {
// this is a disallowed leaf; remove it
let p = curr.parent;
if (p) {
p.nodes = p.nodes.filter(e => e !== curr); // O(n)
}
// move up the structure, pruning dead ancestors
while (p && !p.nodes.length && !allowed.has(p.key)) {
curr = p;
p = curr.parent;
if (p) {
p.nodes = p.nodes.filter(e => e !== curr); // O(n)
}
}
}
}
// clean up temporary parent keys
stack.push(root);
while (stack.length) {
const curr = stack.pop();
if (curr.nodes) {
curr.nodes.forEach(e => {
delete e.parent;
stack.push(e);
});
}
}
return root.nodes;
};
var filteredNodes = removeUnnecessaryNodes(nodes, allowedKeys);
console.log(JSON.stringify(filteredNodes, null, 4));
准备工作包括向外部数组添加一个nodes
键,并将allowedKeys
转换为一组以进行快速查找。然后,DFS结构,为每个键分配新的parent
属性,并将子级推入堆栈。如果到达叶节点且不允许使用其键,请从其父节点列表取消链接。如果然后将父级列表设为空,并且不允许父级节点的键,则也要取消链接并重复,直到到达有效的祖先为止。最后,在结构上执行另一个DFS,以删除临时父属性。
我试图使用一个对象来存储父级属性,但是遇到比较/复制问题,并求助于链表方法。我确信可以解决此问题,保存第二个清理DFS和潜在的密钥冲突。
此外,节点列表过滤过程为O(n),一旦找到子密钥,就可以通过哈希或至少早期的救援来改进。
我确定还有其他人!我很想听听反馈或听听反馈如何在大输入量下实现。