gpt4 book ai didi

java - XML属性互值对的排序算法

转载 作者:数据小太阳 更新时间:2023-10-29 02:11:51 26 4
gpt4 key购买 nike

前提:

我正在尝试寻找或者想出一种算法来解析多个 XML 文件并提取保存在 FROM=XXTO=YY 节点中的启动序列属性。

  • 有数百条记录,可以拆分成对
  • 每个都有 FROMTO 值(value)
  • 每对的 TO 值表明有一个新的对将其作为 来自
  • 因此这些对将链接并创建连续的 FROM-TO 值集。

棘手的部分是,配对可以 split 、分支、分成多个并在特定点加入。

XML:

<FLOW>
<TASK FROM ="B" TO ="C"/>
<TASK FROM ="C" TO ="E1"/>
<TASK FROM ="C" TO ="F1"/>
<TASK FROM ="A1" TO ="A2"/>
<TASK FROM ="A2" TO ="A3"/>
<TASK FROM ="A3" TO ="C"/>
<TASK FROM ="C" TO ="D1"/>
<TASK FROM ="D2" TO ="D3"/>
<TASK FROM ="D1" TO ="D2"/>
<TASK FROM ="E1" TO ="E2"/>
<TASK FROM ="Start" TO ="B"/>
<TASK FROM ="E2" TO ="E3"/>
<TASK FROM ="F1" TO ="F2"/>
<TASK FROM ="F2" TO ="F3"/>
<TASK FROM ="F3" TO ="G"/>
<TASK FROM ="Start" TO ="A1"/>
<TASK FROM ="G" TO ="Finish"/>
<TASK FROM ="E3" TO ="G"/>
<TASK FROM ="D3" TO ="G"/>
</FLOW>

我可以帮助将其形象化,如下图所示:

    Start
/ \
[A1] |
| |
[A2] [B]
| |
[A3] |
\ /
[C]
/ | \
[D1] [E1] [F1]
| | |
[D2] [E2] [F2]
| | |
[D3] [E3] [F3]
\ | /
[G]
|
Finish

期望的输出:

Start, A1, A2, A3, B, C, D1, D2, D3, E1, E2, E3, F1, F2, F3, G, Finish

问题:

我运行了这段代码,但我无法让它以正确的顺序工作并克服 split 。

<# 
INIT Values, starting pair is always considered as combination of tasks where FROM is 'Start'
All task are loaded in pairs, and the sequence begining is assembled.
#>

$Counter = [int]1
$Pairs = $File.SelectNodes('/FLOW/TASK[@FROM="Start"]')
$Tasks = $File.SelectNodes("/FLOW/TASK")

$Sequence = @()
ForEach ($Pair in $Pairs) {
$Sequence += $Pair.TOTASK
}

<#
Scan the tasks and find the matching pair for initial task pair, save it to the output array.
#>

Do {
ForEach ($Task in $Tasks) {
## Main loop counter, on matching pairs
If ($Pairs.FROM -eq $Task.FROM) {
$Counter++
}
## Find current pair's TO in task and flag it as next pair
If ($Pairs.TO -eq $Task.FROM) {
$NextTask = $Task.FROM
$NextPairs = $File.SelectNodes("/FLOW/TASK[@FROM='$NextTask']")
$Sequence += $Task.TO
}
}

## Set new pair to be evaluated
$Pairs = $NextPairs
}
While ($Counter -le $Tasks.Count)

最佳答案

当然,我花了一段时间才为您提出优化的解决方案。开始了:

解决方案(图表!)

好吧,你为什么不像在 OOP 语言中那样处理这个问题:

# Use a hashtable for O(1) lookup for a node by name
$Script:NodeTracker = @{}
class TaskNode {
#==PROPS==================================================|
[System.Collections.ArrayList]$then = @()
[String] $Val
[Bool]$Visited = $false
[Collections.ArrayList]$Parent = @()
#==CONSTRUCTORS===========================================|
TaskNode([String]$Val) {$this.constructor($Val, $null)}
TaskNode([String]$Val, [TaskNode]$Parent) {$this.constructor($Val, $Parent)}
hidden constructor([String]$Val, [TaskNode]$Parent) {
$This.Val = $Val
if ($Parent) {$This.Parents.Add($Parent)}
$Script:NodeTracker.$Val = $This
}

#==METHODS================================================|
[TaskNode] To([String]$Val) {
$Node = $Script:NodeTracker.$Val

# If node doesn't exist, create and track
if (!$Node) {$Node = New-Object TaskNode $Val}
$This.then.Add($Node)
$Node.Parent.Add($This)
return $Node
}
[String] ToString() {return "$($this.val)-$(if($this.Visited){'✓'}else{'✘'})"}
static [String] ReverseModdedBFS([Collections.Queue]$Queue) {
$Output = ""
[Collections.Queue]$NextQueue = @()
do {
while ($Queue.Count) {
$Root = $Queue.Dequeue()
# Write-Host "Root: $Root | Queue: [$Queue] | Next-Queue [$NextQueue] | Non-Visited [$($Root.then | {!$_.Visited})]"
if ($Root.Visited) {continue}
if ($Root.Then.Count -gt 1 -and ($Root.then | {!$_.Visited})) {$NextQueue.Enqueue($Root);continue}
$Root.Visited = $true
$Output += ','+$Root.Val
$Root.Parent | % {
# Write-Host " Enqueuing $_"
$Queue.Enqueue($_)
}
}
If ($Queue.Count -eq 1 -and !$Queue.Peek().Parent.count) {break}
$Queue = $NextQueue
$NextQueue = @()
} while($Queue.Count)
$Out = $Output.Substring(1).split(',')
[Array]::Reverse($Out)
return $Out -join ','
}
#==STATICS=================================================|
static [TaskNode] Fetch([String]$Val) {
$Node = $Script:NodeTracker.$Val
# If node doesn't exist, create and track
if (!$Node) {return (New-Object TaskNode $Val)}
return $Node
}
static [TaskNode[]] GetAll() {
return @($Script:NodeTracker.Values)
}
static [TaskNode] GetStart() {
$Nodes = [TaskNode]::GetAll() | {$_.Parent.count -eq 0}
if ($Nodes.Count -gt 1) {throw 'There is more than one starting node!'}
if (!$Nodes.Count) {throw 'There is no starting node!'}
return @($Nodes)[0]
}
static [TaskNode[]] GetEnds() {
$Nodes = [TaskNode]::GetAll() | {$_.Then.count -eq 0}
if (!$Nodes.Count) {throw 'There is no ending node!'}
return @($Nodes)
}
}

function Parse-Edge($From, $To) {
# Use the safe static accessor so that it will fetch the singleton instance of the node, or create and return one!
[TaskNode]::Fetch($From).To($To)
}

function XML-Main([xml]$XML) {
@($XML.Flow.Task) | % {Parse-Edge $_.From $_.To}
[TaskNode]::ReverseModdedBFS([TaskNode]::GetEnds())
}

测试证明!

我测试如下:

#Create or Find root node 'O'
$Root = [TaskNode]::Fetch('O')

# Set up Chains! Please draw to test
$root.To('A').To('B').To('C').To('H').To('Z').To('M')
$Root.To('D').To('E').To('C').To('H').To('I').To('M')
$Root.To('F').To('G').To('C').To('H').To('J').To('M')
[TaskNode]::Fetch('C').To('K').To('L').To('M')

# Run BFS!
[TaskNode]::ReverseModdedBFS([TaskNode]::GetEnds())

测试输出

Root: M-✘ | Queue: [] | Next-Queue [] | Non-Visited []
Enqueuing Z-✘
Enqueuing I-✘
Enqueuing J-✘
Enqueuing L-✘
Root: Z-✘ | Queue: [I-✘ J-✘ L-✘] | Next-Queue [] | Non-Visited []
Enqueuing H-✘
Root: I-✘ | Queue: [J-✘ L-✘ H-✘] | Next-Queue [] | Non-Visited []
Enqueuing H-✘
Root: J-✘ | Queue: [L-✘ H-✘ H-✘] | Next-Queue [] | Non-Visited []
Enqueuing H-✘
Root: L-✘ | Queue: [H-✘ H-✘ H-✘] | Next-Queue [] | Non-Visited []
Enqueuing K-✘
Root: H-✘ | Queue: [H-✘ H-✘ K-✘] | Next-Queue [] | Non-Visited []
Enqueuing C-✘
Enqueuing C-✘
Enqueuing C-✘
Root: H-✓ | Queue: [H-✓ K-✘ C-✘ C-✘ C-✘] | Next-Queue [] | Non-Visited []
Root: H-✓ | Queue: [K-✘ C-✘ C-✘ C-✘] | Next-Queue [] | Non-Visited []
Root: K-✘ | Queue: [C-✘ C-✘ C-✘] | Next-Queue [] | Non-Visited []
Enqueuing C-✘
Root: C-✘ | Queue: [C-✘ C-✘ C-✘] | Next-Queue [] | Non-Visited []
Enqueuing B-✘
Enqueuing E-✘
Enqueuing G-✘
Root: C-✓ | Queue: [C-✓ C-✓ B-✘ E-✘ G-✘] | Next-Queue [] | Non-Visited []
Root: C-✓ | Queue: [C-✓ B-✘ E-✘ G-✘] | Next-Queue [] | Non-Visited []
Root: C-✓ | Queue: [B-✘ E-✘ G-✘] | Next-Queue [] | Non-Visited []
Root: B-✘ | Queue: [E-✘ G-✘] | Next-Queue [] | Non-Visited []
Enqueuing A-✘
Root: E-✘ | Queue: [G-✘ A-✘] | Next-Queue [] | Non-Visited []
Enqueuing D-✘
Root: G-✘ | Queue: [A-✘ D-✘] | Next-Queue [] | Non-Visited []
Enqueuing F-✘
Root: A-✘ | Queue: [D-✘ F-✘] | Next-Queue [] | Non-Visited []
Enqueuing O-✘
Root: D-✘ | Queue: [F-✘ O-✘] | Next-Queue [] | Non-Visited []
Enqueuing O-✘
Root: F-✘ | Queue: [O-✘ O-✘] | Next-Queue [] | Non-Visited []
Enqueuing O-✘
Root: O-✘ | Queue: [O-✘ O-✘] | Next-Queue [] | Non-Visited []
Root: O-✓ | Queue: [O-✓] | Next-Queue [] | Non-Visited []
Root: O-✓ | Queue: [] | Next-Queue [] | Non-Visited []
O,F,D,A,G,E,B,C,K,H,L,J,I,Z,M

解释和算法

我们使用 graph使用一些巧妙的 OOP 技巧将所有边相互绘制。然后我们从所有汇节点(即没有子节点的节点)反向遍历图。我们一直在做BFS直到我们遇到一个节点:

  • 有超过1个 child
  • AND,有超过 0 个未访问的后代
  • 如果是,请将其添加到下一轮 BFS!

重复此操作直到您当前和 future 的队列为空,在这种情况下,您的输出现已完成。现在:

  • 以逗号分隔
  • 反向数组(因为我们做了反向遍历)
  • 打印输出!

关于java - XML属性互值对的排序算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48812185/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com