这比简单的左递归或尾调用递归要复杂一些。所以我想知道如何消除这种递归。正如您在下面看到的,我已经保留了自己的堆栈,因此该函数不需要参数或返回值。但是,它仍在将自身向上(或向下)调用到某个级别,我想将其变成一个循环,但现在已经为此挠头了一段时间。
这是简化的测试用例,用 printf("dostuff at level #n") 消息替换所有“真实逻辑”。这是在 Go 中,但该问题适用于大多数语言。使用循环和 goto 是完全可以接受的(但我玩过这个,它变得令人费解、失控并且一开始似乎不可行);但是,应避免使用额外的辅助函数。我想我应该把它变成某种简单的状态机,但是......哪个?;)
至于实用性,这是以每秒大约 2000 万次的速度运行(稍后堆栈深度的范围可以从 1 到 25 最大)。在这种情况下,维护我自己的堆栈必然比函数调用堆栈更稳定/更快。(此函数中没有其他函数调用,只有计算。)此外,没有垃圾生成 = 没有垃圾收集。
所以这里是:
func testRecursion () {
var root *TMyTreeNode = makeSomeDeepTreeStructure()
// rl: current recursion level
// ml: max recursion level
var rl, ml = 0, root.MaxDepth
// node: "the stack"
var node = make([]*TMyTreeNode, ml + 1)
// the recursive and the non-recursive / iterative test functions:
var walkNodeRec, walkNodeIt func ();
walkNodeIt = func () {
log.Panicf("YOUR ITERATIVE / NON-RECURSIVE IDEAS HERE")
}
walkNodeRec = func () {
log.Printf("ENTER LEVEL %v", rl)
if (node[rl].Level == ml) || (node[rl].ChildNodes == nil) {
log.Printf("EXIT LEVEL %v", rl)
return
}
log.Printf("PRE-STUFF LEVEL %v", rl)
for i := 0; i < 3; i++ {
switch i {
case 0:
log.Printf("PRECASE %v.%v", rl, i)
node[rl + 1] = node[rl].ChildNodes[rl + i]; rl++; walkNodeRec(); rl--
log.Printf("POSTCASE %v.%v", rl, i)
case 1:
log.Printf("PRECASE %v.%v", rl, i)
node[rl + 1] = node[rl].ChildNodes[rl + i]; rl++; walkNodeRec(); rl--
log.Printf("POSTCASE %v.%v", rl, i)
case 2:
log.Printf("PRECASE %v.%v", rl, i)
node[rl + 1] = node[rl].ChildNodes[rl + i]; rl++; walkNodeRec(); rl--
log.Printf("POSTCASE %v.%v", rl, i)
}
}
}
// test recursion for reference:
if true {
rl, node[0] = 0, root
log.Printf("\n\n=========>RECURSIVE ML=%v:", ml)
walkNodeRec()
}
// test non-recursion, output should be identical
if true {
rl, node[0] = 0, root
log.Printf("\n\n=========>ITERATIVE ML=%v:", ml)
walkNodeIt()
}
}
更新——经过这里的一些讨论,并进一步思考:
我刚刚编写了以下伪代码,理论上应该可以满足我的需要:
curLevel = 0
for {
cn = nextsibling(curLevel, coords)
lastnode[curlevel] = cn
if cn < 8 {
if isleaf {
process()
} else {
curLevel++
}
} else if curLevel == 0 {
break
} else {
curLevel--
}
}
当然,棘手的部分是为我的自定义用例填写 nextsibling() 。但就像在保持我需要的深度优先遍历顺序的同时消除内部递归的一般解决方案一样,这个粗略的轮廓应该以某种形式这样做。