我需要一种算法来遍历带有可选节点和替代节点的树,以计算所有可能的路径
I need an algorithm to walk a tree with optional and alternative nodes to calculate all possible paths
给定以下树,它是一种 Backus-Nauf 形式的符号,其中
|表示或(所以B是F或G)和[]表示可选(所以H是可选的)
Def: A B C
A: D E
B: F | G
C: [H] I
D: a b
E: c d
F: e f
G: g h
H: i j
I: k l
a: a
b: b
c: c
d: d
e: e
f: f
g: g
h: h
i: i
j: j
k: k
l: l
可以看作是
Def
A B C
D E F | G [H] I
a b c d e f g h i j k l
我需要遍历提取叶节点的树并转换为下面给出可能路径的树
Def
a
b
c
d
e
f
i
j
k
l
k
l
g
h
i
j
k
l
k
l
所以可能的路径是
abcdefijkl
abcdefkl
abcdghijkl
abcdghkl
我有一个 repo 有一个失败的 C# 单元测试(设置树并调用基本的递归 walker),希望它能阐明我想要实现的目标。
我想不通的是如何在可选节点和替代节点处分支,同时保持正确的叶子以将后续叶子附加到。
非递归广度优先搜索可能看起来像这些线(伪代码)。通过调用 findAllLeafPaths(Def)
:
开始
var allPathsFound = {}
function findAllLeafPaths(startNode) {
var tokenSequenceQueue = {
createTokenSequenceFrom(startNode)
}
while (tokenSequenceQueue.isNotEmpty()) {
var tokenSequence = tokenSequenceQueue.removeFirst()
var allLeaves = true
for (every token T in tokenSequence) {
if isLeafNode(T)
continue
else if T's rule is "T: Y Z" {
allLeaves = false
tokenSequenceQueue.append(tokenSequence.replace(T, Y + Z))
} else if T's rule is "T: [Y] Z" {
allLeaves = false
tokenSequenceQueue.append(tokenSequence.replace(T, Y + Z))
tokenSequenceQueue.append(tokenSequence.replace(T, Z))
} else if T's rule "T: Y | Z" {
allLeaves = false
tokenSequenceQueue.append(tokenSequence.replace(T, Y))
tokenSequenceQueue.append(tokenSequence.replace(T, Z))
}
}
if (allLeaves) {
allPathsFound.add(tokenSequence)
}
}
}
这里还有一个递归深度优先的版本。我更喜欢第一个,因为递归使您的堆栈受到结果路径的最大可能长度的支配:
var allPathsFound = {}
function toLeafNodes(tokenSequence) {
var allLeaves = true
for every token T in tokenSequence {
if isLeafNode(T)
continue
else if T's rule is "T: Y Z" {
allLeaves = false
toLeafNodes(tokenSequence.replace(T, Y + Z)
} else if T's rule is "T: [Y] Z" {
allLeaves = false
toLeafNode(tokenSequence.replace(T, Y + Z)
toLeafNode(tokenSequence.replace(T, Z)
} else if T's rule "T: Y | Z" {
allLeaves = false
toLeafNode(tokenSequence.replace(T, Y)
toLeafNode(tokenSequence.replace(T, Z)
}
}
if (allLeaves) {
allPathsFound.add(tokenSequence)
}
}
[编辑] 非递归版本目前一次替换一个,并立即将结果序列放入队列。它可以进一步优化,一次完成所有可能的替换。
还有另一种方法可以根据您的定义构建树。考虑:
Def: A B C
A: D E
B: F | G
C: [H] I
开始于
A
\
B
\
C
然后把A换成D E:
D
\
E
\
B
\
C
对 B(替换为 F | G)和 C(替换为 [H] I)做同样的事情,你会得到:
D
\
E
/ \
F G
/ \ / \
I H I H
\ \
I I
现在,如果您对该树进行递归深度优先遍历,您将获得有效字符串:
D E F I
D E F H I
D E G I
D E G H I
并且在输出字符串时,您可以将 D 替换为 "a b",等等。
我一步一步地展示了它,但你可以一次完成所有操作。
给定以下树,它是一种 Backus-Nauf 形式的符号,其中 |表示或(所以B是F或G)和[]表示可选(所以H是可选的)
Def: A B C
A: D E
B: F | G
C: [H] I
D: a b
E: c d
F: e f
G: g h
H: i j
I: k l
a: a
b: b
c: c
d: d
e: e
f: f
g: g
h: h
i: i
j: j
k: k
l: l
可以看作是
Def
A B C
D E F | G [H] I
a b c d e f g h i j k l
我需要遍历提取叶节点的树并转换为下面给出可能路径的树
Def
a
b
c
d
e
f
i
j
k
l
k
l
g
h
i
j
k
l
k
l
所以可能的路径是
abcdefijkl
abcdefkl
abcdghijkl
abcdghkl
我有一个 repo 有一个失败的 C# 单元测试(设置树并调用基本的递归 walker),希望它能阐明我想要实现的目标。
我想不通的是如何在可选节点和替代节点处分支,同时保持正确的叶子以将后续叶子附加到。
非递归广度优先搜索可能看起来像这些线(伪代码)。通过调用 findAllLeafPaths(Def)
:
var allPathsFound = {}
function findAllLeafPaths(startNode) {
var tokenSequenceQueue = {
createTokenSequenceFrom(startNode)
}
while (tokenSequenceQueue.isNotEmpty()) {
var tokenSequence = tokenSequenceQueue.removeFirst()
var allLeaves = true
for (every token T in tokenSequence) {
if isLeafNode(T)
continue
else if T's rule is "T: Y Z" {
allLeaves = false
tokenSequenceQueue.append(tokenSequence.replace(T, Y + Z))
} else if T's rule is "T: [Y] Z" {
allLeaves = false
tokenSequenceQueue.append(tokenSequence.replace(T, Y + Z))
tokenSequenceQueue.append(tokenSequence.replace(T, Z))
} else if T's rule "T: Y | Z" {
allLeaves = false
tokenSequenceQueue.append(tokenSequence.replace(T, Y))
tokenSequenceQueue.append(tokenSequence.replace(T, Z))
}
}
if (allLeaves) {
allPathsFound.add(tokenSequence)
}
}
}
这里还有一个递归深度优先的版本。我更喜欢第一个,因为递归使您的堆栈受到结果路径的最大可能长度的支配:
var allPathsFound = {}
function toLeafNodes(tokenSequence) {
var allLeaves = true
for every token T in tokenSequence {
if isLeafNode(T)
continue
else if T's rule is "T: Y Z" {
allLeaves = false
toLeafNodes(tokenSequence.replace(T, Y + Z)
} else if T's rule is "T: [Y] Z" {
allLeaves = false
toLeafNode(tokenSequence.replace(T, Y + Z)
toLeafNode(tokenSequence.replace(T, Z)
} else if T's rule "T: Y | Z" {
allLeaves = false
toLeafNode(tokenSequence.replace(T, Y)
toLeafNode(tokenSequence.replace(T, Z)
}
}
if (allLeaves) {
allPathsFound.add(tokenSequence)
}
}
[编辑] 非递归版本目前一次替换一个,并立即将结果序列放入队列。它可以进一步优化,一次完成所有可能的替换。
还有另一种方法可以根据您的定义构建树。考虑:
Def: A B C
A: D E
B: F | G
C: [H] I
开始于
A
\
B
\
C
然后把A换成D E:
D
\
E
\
B
\
C
对 B(替换为 F | G)和 C(替换为 [H] I)做同样的事情,你会得到:
D
\
E
/ \
F G
/ \ / \
I H I H
\ \
I I
现在,如果您对该树进行递归深度优先遍历,您将获得有效字符串:
D E F I
D E F H I
D E G I
D E G H I
并且在输出字符串时,您可以将 D 替换为 "a b",等等。
我一步一步地展示了它,但你可以一次完成所有操作。