【问题标题】:How to find the end of a tree process in vba如何在vba中找到树进程的结尾
【发布时间】:2014-06-02 22:26:54
【问题描述】:

我有一个 Excel 工作表,其中包含与每个操作相关的操作和时间。而且这些操作是相互联系的,有的有后继,有的没有。

我创建了一个宏来查找操作的直接后继,它运行良好。

但我想做的是找到一个操作的所有后继者。我的意思是计算后继者的后继者等等,因为我想看看如果我取消或停止这个操作会影响多少时间。

这是它的外观示例:

   Lvl1   Lvl2  Lvl3  Lvl4   Lvl5
                OP3---OP5----OP6
               /
              /
           OP1
          /   \
         /     \
        /       OP4
    OP0
        \ 
         \
          \
           OP2---OP7

在此示例中,要从一个级别转到另一个级别,我必须运行我的宏(如果一个操作没有更多后续操作,我的宏将返回一个空数组)。

所以,目的是把所有这些操作的时间相加来找到结果。

但我不知道如何让我的循环在一个操作没有后续操作时停止,然后转到下一个操作。

例如这里,我怎么知道当我到达 OP6 时我必须回到 OP4

PS:这是我的宏的简化版本(我的文件中没有每个操作的后继者,只有前辈,这就是为什么我必须查看文件以查找我想要的操作是否在每个操作的前辈列表)

OP_number = "ER345RET"

For i = 2 To File_size
    Predecessors = Split(Worksheets(1).Range("S" & i).Value, ";")
    For j = 0 To UBound(Predecessors)
        If Predecessors(j) = OP_number Then
            Successors(q) = Worksheets(1).Range("F" & i).Value
            q = q + 1
        End If
    Next j
Next i

【问题讨论】:

  • 我的代码只是给了我一个操作的后继者。我只想知道正确迭代它的逻辑。让我们调用我的宏 find_successors(OP) 并返回数组 Successors(),其大小等于后继数
  • 查看递归。 YouTube 上的 Computerphile 有一个很好的“什么是递归?”视频。如果不查看您的代码,我们不可能为您提供帮助。
  • 我编辑了我的帖子,并把我的后继宏的简化版放了
  • 根据您的代码,“OP5”的前身可能在单元格中写为“OP0;OP1;OP3”,对吗?如果是这样,它们是否总是按顺序排列,最右边是父节点(“OP3”是“OP5”的父节点),最左边是根节点(“OP0”)?
  • 另外,每个操作的成本是否相同,或者是否有一列数据表明每个操作的成本?

标签: vba excel


【解决方案1】:

递归很可能是您完成此任务的最佳选择。递归函数看起来像这样(伪代码):

'Recursive function returns count of successors
Function getSuccessor(predecessor As String) As Long
    'Check if this predecessor has a successor
        'If Yes then call self (getSuccessor) with new predecessor
            getSuccessor = getSuccessor(myNewSuccessor)

        'If No set exit condition
            getSuccessor = 1  'one increases the count for this level
End Function

您似乎在尝试构建每个继任者的数组。您可以通过在递归调用 getSuccessor 函数之前将新项目添加到数组中来做到这一点。您可能需要一个全局数组来存储它。

'If Yes then call self (getSuccessor) with new predecessor
    Successors(x) = myNewSuccessor
    getSuccessor = getSuccessor(myNewSuccessor)

主函数看起来像这样:

Sub recursion()
    Dim count As Integer
    OP_number = "ER345RET"

    For i = 2 To File_size
        'set topLevelPredecessor
        count = getSuccessor(topLevelPredecessor)
    Next i

    MsgBox ("Total Levels of successors is: " & count)
End Sub

【讨论】:

  • 感谢解答,我明白递归的原理,函数会寻找后继者,直到没有后继者为止。但是我怎样才能让程序明白一旦找到分支的结尾就必须回到最后一个节点呢? (在我的示例中,一旦达到 OP6,它必须查看 OP4 是否有继任者等)
  • 递归通过结束条件为您处理这个问题。这有点难以解释。 This link 可能比我能更好地解释它。您能否编辑原始帖子并提供数据的屏幕截图?这将帮助我了解所有内容的格式。
【解决方案2】:

这样的数据:

F       S
OP0 
OP1     OP0
OP2     OP0
OP3     OP1
OP4     OP1
OP5     OP3
OP6     OP5
OP7     OP2

那么这段代码会将所有后继者放入一个数组中

Sub Main()

    Dim vaPreds As Variant
    Dim aSuccs() As String
    Dim vaOps As Variant
    Dim lCnt As Long
    Dim i As Long

    'Create a two-dimensional array of predecessors
    'from column S
    vaPreds = Sheet1.Range("S2:S8").Value
    'Create a two-dim array of operations from
    'column F
    vaOps = Sheet1.Range("F1:F8").Value

    'Call the function that will load the successors
    'into the aSuccs() array variable
    FindPreds "OP0", vaPreds, aSuccs, vaOps, lCnt

    'Loop through the final successors array and
    'print them to the Immediate Window
    For i = LBound(aSuccs) To UBound(aSuccs)
        Debug.Print aSuccs(i)
    Next i

End Sub

Sub FindPreds(ByVal sOpStart As String, ByRef vaPreds As Variant, ByRef vaSuccs As Variant, ByRef vaOps As Variant, ByRef lCnt As Long)

    Dim vaSplit As Variant
    Dim i As Long, j As Long

    'Loop through all the predecessors
    For i = LBound(vaPreds, 1) To UBound(vaPreds, 1)
        'Split the predecessors on semi colon for cells
        'where there are more than one.
        vaSplit = Split(vaPreds(i, 1), ";")
        'Loop through the split predecessors
        For j = LBound(vaSplit) To UBound(vaSplit)
            'If the predecessor is the operation I'm looking
            'for, add the operation to the successors array
            If vaSplit(j) = sOpStart Then
                lCnt = lCnt + 1
                ReDim Preserve vaSuccs(1 To lCnt)
                vaSuccs(lCnt) = vaOps(i + 1, 1)

                'Go find any successors for the operation you just
                'added to the successors array
                FindPreds vaOps(i + 1, 1), vaPreds, vaSuccs, vaOps, lCnt

            End If
        Next j
    Next i

End Sub

【讨论】:

  • 感谢您的回答,不幸的是,我的数据结构并非如此,对于每个操作,我都有一个单元格“前身”,其中列出了此操作的前身,例如“OP1;OP2;OP3”(即为什么我必须使用 Split() 来提取它们)。你也可以在你的代码中添加一些 cmets,因为我无法理解它是如何工作的
  • 在你的图表中,一切都只有一个前任。你能更新你的图表以显示它的样子吗?或者显示一些示例数据,以便我们查看结构。
【解决方案3】:

我终于找到了解决问题的方法。

我决定一次遍历每一个分支,而不是一个接一个地遍历树的每个分支。当我没有任何继任者时停止迭代。

这是我写的一段代码:

q = 1
qstop = 1

Opdaughters(1, 0) = OP_number

For i = 2 To Size
    If Worksheets("XXX").Range("J" & i) = OP Then
        Opfilles(2, 0) = Worksheets("XXX").Range("O" & i)
    End If
Next i

Call Macro(OP_number, q, Opdaugthers)

Buffer() = Opdaughters()

While q <> qstop
    retenue = q
    For i = qstop To q
        Call Macro(Buffer(1, i), q, Opdaughters)
        ReDim Preserve Buffer(2, UBound(Opdaughters, 2))
    Next i
    qstop = retenue
    Buffer() = Opdaughters()
Wend

基本上,我通过在其末尾添加继任者来使我的表 OPdaughters 增长。然后当没有更多的后继者被添加到我的表中时(即当 q=qstop 时),这意味着我到达了我的进程的结尾。

这是我的表 OPdaughters 在迭代中的样子:

Iteration0    Iteration1    Iteration2
    OP0          OP0           OP0
                 OP1           OP1
                 OP2           OP2
                 OP3           OP3
                               OP11
                               OP12
                               OP21
                               OP31
                               OP32
                               OP33

【讨论】:

    猜你喜欢
    • 2013-08-21
    • 2018-01-05
    • 2014-12-09
    • 1970-01-01
    • 2021-10-21
    • 1970-01-01
    • 2013-01-25
    • 2015-06-23
    • 1970-01-01
    相关资源
    最近更新 更多