![](/img/trans.png)
[英]How to make the fastest possible bottom-up tree transformer in JavaScript? Should I manage memory on my own?
[英]How to convert bottom-up recursive algorithm to iterative stack in JavaScript
给出以下算法:
console.log(JSON.stringify(create(0), null, 2)) function create(i) { if (i == 5) return return new Klass(i, create(i + 1), create(i + 1)) } function Klass(i, l, r) { this.i = i this.l = l this.r = r }
在递归创建所有子项之后,它最后在create(0)
中create(0)
Klass
。 因此它首先创建叶节点,然后将其传递给父节点等。
想知道如何使用堆栈而不递归。 让我的头受伤:) 我理解如何使用堆栈从上到下创建,但不是自下而上。 对于自上而下,它基本上是这样的:
var stack = [0]
while (stack.length) {
var i = stack.pop()
// do work
stack.push(children)
}
从下到上,我看不出它应该如何运作。 这是我被卡住的地方:
function create(i) {
var stack = []
stack.push([i, 'open'])
stack.push([i, 'close'])
while (stack.length) {
var node = stack.pop()
if (node[1] == 'open') {
stack.push([ node[0] + 1, 'open' ])
stack.push([ node[0] + 1, 'close' ])
} else {
// ?? not sure how to get to this point
var klass = new Klass(node[0], node[2], node[3])
// ??
}
}
}
将任何递归代码机械转换为堆栈机器并非易事。 自动状态转换产生非常复杂的代码,只需考虑C#-s或BabelJS-s生成器。 但可以肯定的是,它可以完成,但是你需要可变的堆栈帧和/或寄存器。 让我们看看我们面临的问题:
我们必须在堆栈本身上存储一些状态变量/指令指针。 这就是您使用"open"
和"close"
标记进行模拟的内容。
有很多方法:
out
参数 使用可变堆栈帧和结果寄存器,转换后的代码看起来像这样:
console.log(JSON.stringify(create(0), null, 2)) function Klass(i, l, r) { this.i = i this.l = l this.r = r } function Frame(i) { this.ip = 0; this.i = i; this.left = null; } function create(i) { var result; var stack = [new Frame(i)]; while (stack.length > 0) { var frame = stack[stack.length - 1]; switch (frame.ip) { case 0: if (frame.i === 5) { result = undefined; stack.pop(); break; } stack.push(new Frame(frame.i + 1)); frame.ip = 1; break; case 1: frame.left = result; stack.push(new Frame(frame.i + 1)); frame.ip = 2; break; case 2: result = new Klass(frame.i, frame.left, result); stack.pop(); break; } } return result; }
这是使用两个堆栈的解决方案。
假设我们总是在离开孩子之前计算出正确的孩子,我们需要一种方法来存储正确孩子的结果。 可以将它存储在原始堆栈上,但它会很复杂,因为该堆栈也用于计算左子。 所以我使用另一个堆栈来存储正确孩子的结果。
有三种状态:
当它看到状态finish work
的节点时,它将检查下一个节点的状态是否need merge
:
finish work
console.log(JSON.stringify(create(2, 5), null, 2)) function Klass(i, l, r) { this.i = i; this.l = l; this.r = r; } function create(i, growto) { var stack = []; var cache = []; stack.push([i, 'need work']); while (stack.length && stack[0][1] !== 'finish work') { var cur = stack.pop(); var val = cur[0]; var status = cur[1]; if (status === 'need work') { if (val !== growto) { stack.push([val, 'need merge']); stack.push([val + 1, 'need work']); stack.push([val + 1, 'need work']); } else { stack.push([val, 'finish work']); } } else if (status === 'finish work') { if (stack[stack.length - 1][1] !== 'need merge') { cache.push(cur); } else { var root = stack.pop()[0]; var left = cur[0]; var right = cache.pop()[0]; stack.push([new Klass(root, left, right), 'finish work']); } } } return stack.pop()[0]; }
这样的事情怎么样:
console.log(JSON.stringify(create(4), null, 2)) function create(depth) { let n = Math.pow(2, depth); let nodes = []; for (let i = 0; i < n; i++) nodes.push(new Klass(depth)); for (depth--; depth >= 0; depth--) { let next = []; while (nodes.length > 0) next.push(new Klass(depth, nodes.pop(), nodes.pop())); nodes = next; } return nodes[0]; } function Klass(i, l, r) { this.i = i this.l = l this.r = r }
获得相同结果的调用将是create(4);
。 它不是完全相同的创建顺序,它从下到上创建节点,而递归就像:
7
3 6
1 2 4 5
您还可以使用堆栈模仿此行为:
console.log(JSON.stringify(create(4), null, 2)) function create(depth) { let stack = [{depth: 0}] for (;;) { let i = stack.length - 1 let cur = stack[i] if (typeof cur.left === 'undefined') { if (cur.depth < depth) { stack.push({depth: cur.depth + 1, parent: i, pos: 'right'}) stack.push({depth: cur.depth + 1, parent: i, pos: 'left'}) } else { stack[cur.parent][cur.pos] = new Klass(cur.depth) stack.pop() } } else { let node = new Klass(cur.depth, cur.left, cur.right) if (cur.depth == 0) return node stack[cur.parent][cur.pos] = node stack.pop() } } } function Klass(i, l, r) { this.i = i this.l = l this.r = r }
右侧节点首先在堆栈上推送,然后在左侧节点上推送,以便左侧节点在堆栈中更高并首先进行处理。
让我们先从只是i
S:
function create(i) { console.log(i) if (i == 3) return return new Klass(i, create(i+1), create(i+1)) } function Klass(i, l, r) { this.i = i this.l = l this.r = r } console.log(JSON.stringify(create(0))) console.log('\\nStack version:') let stack = [0]; while (stack.length){ let i = stack.pop(); console.log(i); if (i < 3) stack.push(i + 1, i + 1); }
有很多方法可以使用迭代生成的i
s顺序; 从把它们全部推到一个阵列,然后跟踪任务向后; 使用i
创建一个新的Klass并通过引用传递它,实质上将过程变为自上而下。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.