这比简单的左递归或尾调用递归更复杂。所以我想知道如何消除这种递归。我已经保留了自己的堆栈,如下所示,因此该函数不需要params或返回值。然而,它仍然将自己调高(或降低)到某个水平,我想把它变成一个循环,但是现在已经在我的头上刮了一段时间。
这是简化的测试用例,用printf(“在#n级别的dostuff”)消息替换所有“真实逻辑”。这是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()。但是作为消除内部递归同时保持我需要的深度优先遍历顺序的一般解决方案,这个粗略的轮廓应该以某种形式这样做。
答案 0 :(得分:1)
由于您的递归代码看起来有点奇怪,我不确定我是否理解您想要做什么。但是,如果我理解了TMyTreeNode的结构,那么这就是我对非递归版本所做的。
// root is our root node
q := []*TMyTreeNode{root}
processed := make(map[*TMyTreeNode]bool
for {
l := len(q)
if l < 1 {
break // our queue is empty
}
curr := q[l - 1]
if !processed[curr] && len(curr.childNodes) > 0 {
// do something with curr
processed[curr] = true
q = append(q, curr.childNodes...)
continue // continue on down the tree.
} else {
// do something with curr
processed[curr] = true
q := q[:l-2] // pop current off the queue
}
}
注意:这将任意深入到结构中。如果这不是您想要的,则需要进行一些修改。