我有一项任务。我必须使用BFS在XML中找到两个节点之间的最短路径。 XML是'Romeo&朱丽叶'(你可以在这里找到它:enter link description here)。
我必须找到朱丽叶和其他人之间的最短路径。图中的节点应该是扬声器,边缘 - 两个扬声器一起出现的语音。例如:如果朱丽叶与X交谈,距离将为1.但如果朱丽叶与X交谈(但不与Y交谈)并且X与Y交谈,则距离为2.
结果应该是这样的(仅作为示例):
<speaker name="Romeo"><distance>1</distance></speaker>
<speaker name="Tybalt"><distance>3</distance></speaker>
<speaker name="Benvolio"><distance>2</distance></speaker>
...
我准备相同的代码,但我对BFS没有任何想法:
declare function local:BFS($queue,$speakers,$visited,$step)
{
let $u :=
for $i in $queue
for $w in local:neighbour($i)
(..)
return ($u)
};
declare function local:neighbour($person as xs:string)
{
let $file := "r_and_j.xml"
let $speakers :=
distinct-values(doc($file)/PLAY/ACT/SCENE/SPEECH/SPEAKER)
let $sp-query :=
for $y in $speakers
where not($person=$y)
return if(doc($file)/PLAY/ACT/SCENE[SPEECH[SPEAKER=$person] and SPEECH[SPEAKER=$y]])
then
$y
else ()
return $sp-query
};
功能'邻居'从邻居返回节点(与从参数中获取的人交谈的发言人)。
答案 0 :(得分:1)
一个月前有一个very similar question。请仔细阅读答案,了解嵌套for
循环无法按照您的想法运行的原因。
对于您的特定任务,查看BFS algorithm的每个“回合”(处理在特定步骤之后找到的所有节点)而不是使用简单队列会更合适。我还会在开头提取一次邻居并以更容易访问的形式存储它们。然后算法看起来像这样:
declare function local:round($n, $last-round, $out) {
if(empty($last-round)) then (
(: last round found no new persons, we are finished :)
$out
) else (
let $current-round :=
(: iterate over all persons found in the last round :)
fold-left($last-round, (), function($current, $speaker) {
(: iterate over their neighbors :)
fold-left(
(: ignore persons found in previous rounds :)
$persons[@name=$speaker/@name]/neighbor/@name[not(. = $out/@name)],
$current,
function($current2, $neighbor) {
if($current2/speaker[@name = $neighbor]) then (
(: person was already found this round, ignore :)
$current2
) else (
(: append newly found person to this round's output :)
$current2,
<speaker name="{$neighbor}"><distance>{$n}</distance></speaker>
)
})
})
return local:round($n + 1, $current-round, ($out, $last-round))
)
};
可以看到整个查询here。它需要XQuery 3.0并使用最新版本的BaseX进行测试。
答案 1 :(得分:-1)
这里最好的选择可能是远离XML并在像Jena这样的图形数据库中建模您的实体。从那里,您可以轻松地通过两个连接建立最短路径,并为您的关系增加权重。