xquery - BFS和两个节点之间的最短路径

时间:2014-06-12 22:01:45

标签: xml xquery

我有一项任务。我必须使用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
};

功能'邻居'从邻居返回节点(与从参数中获取的人交谈的发言人)。

2 个答案:

答案 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这样的图形数据库中建模您的实体。从那里,您可以轻松地通过两个连接建立最短路径,并为您的关系增加权重。