如何逐步构建非二进制树(具有依赖关系)

时间:2016-01-08 15:15:04

标签: c++ algorithm tree dependencies

简短说明

我需要从一个彼此依赖的项目列表中构建一个非二叉树(语言现在并不重要,但最好是在C ++中),但非重复且不循环。< / p>

从文件中读取节点的数据并逐步插入树中。 令人不安的部分是如何处理那些没有父节点但仍满足插入节点依赖性的节点。

详细说明

粗略轮廓

赋值很简单:在非二叉树中表示一堆任务和子任务。 这个赋值很容易理解和实现,如果不是很小的条件:必须以递增方式生成任务列表,树中的节点也是如此。

方案

任务是异步生成的,一旦收到某个任务的数据,就必须将其添加到树中。 这是&#34;模拟&#34;通过读取一个csv文件,该文件在每行中都有一些具有某些数据的任务,最重要的是PID和PPID属性。

读取并解析了一行后,正在创建一个Task并将其插入到树中。 树应该遵循两个简单的规则自动解析依赖关系:

  1. 仅在满足依赖关系时显示节点(即之前插入父节点时),但记住(现在孤立的)节点。
  2. 每当添加一个任务(节点)时,检查它是否是上述任何一个孤立的孤儿之一的父节点,如果规则#1没有被侵犯,则协调节点。
  3. 请忽略这种情况背后的错误逻辑:通常,不存在ParentTask的任何SubTask(至少在单片内核设计中)。 虽然任务列表确实包含了对树进行建模所需的ParentTasks,但未知何时读取ParentNode-Data并将其插入树中。

    期望的结果

    下面是一个显示&#34;原始数据&#34;的图,这是一个(未排序的)任务列表,它是在向列表添加一个又一个Task时逐步创建的。 树表示到目前为止已插入的任务子集:

    Figure showing the desired output

    请记住,树完全是赤裸裸的&#34;直到插入了PID 1,2和3的任务,因为其他节点都依赖于它们。

    到目前为止我做了什么

    我编写了一个包含三个粗略组件的Qt-C ++代码:

    • 任务树,其中包含根节点(没有任何任务数据的节点)
    • TaskNode,其中包含一个用于保存任务数据的字段和一个 QList&lt; TaskNode&gt; ,简单来说就是一个引用子节点的TaskNodes向量
    • 任务具有相关属性(如pid和ppid)

    如果父节点已经存在,则插入TaskNode没有问题。 这仅适用于完美的世界,其中任务按其各自的依赖关系进行排序,并且确定了要添加的任务量。

    我不必告诉你这样的场景极不可能,所以树的创建必须记住任何孤立的节点(这个节点还没有父节点,但是)

    我已经解决了这个&#34;记忆&#34;以不同的方式,但完全失败,因为我无法绕过它背后的算法。 我有两个最有希望的想法是:

    1. 将每个孤立节点插入到矢量中。插入父节点后,检查它是否在Orphan-Vector中有子节点并进行协调。为新创建的子树递归执行此操作以匹配所有可能性。
    2. 将PPID分配给树的RootNode,最高的一个为0。当出现孤立节点时,创建一个新的TaskTree,将孤儿的PPID分配给新创建的树并向其添加孤立。 如果几个孤儿与其中一棵树匹配,这会创建可以退出复杂的子树。在每个插入的节点之后,尝试将子树协调到根树。
    3. 不幸的是,由于几次自发的SIGSEGV以及由于递归等原因而出现的其他问题,我不得不放弃继续这两个概念。

      所以最后我在这里试图找到一种方法来实际完成这项工作,而不是通过假设和其他作弊来减少问题的复杂性......

      你们和女士们是否知道我可以使用哪种算法解决这个问题,或者甚至是哪种类型的问题?

2 个答案:

答案 0 :(得分:1)

方法2是正确的方法。您缺少的部分是您需要一个名为unordered_map的{​​{1}},它将尚未看到的父节点映射到等待它的{1}}子树。您需要将类似的映射node_needed映射到已经看到的节点的关联树。

然后,当您看到节点时,执行以下操作:

vector

假设没有错误(HAH!错误是生活的一部分......),这应该有用。

答案 1 :(得分:0)

经过一些刻意的设计变更后,我想出了 - 我认为 - 最简单的实现方法:

InsertTask(Task newTask)
{
    Task parentTask = searchTreeForParent(newTask->ppid)
    If (parentTask not found)
    {
        parentTask = treeRootNode;
    }

    If (treeRootNode has children)
    {
        For (every children in treeRootNode: child)
        {
            If (child->ppid != treeRootNode->pid AND child->ppid == newTask->pid)
            {
                newTask->addChild(child)
                treeRootNode->remove(child)
            }
        }
    }

    parentTask->addChild(newTask)
}

它背后的算法非常简单:如果还没有父节点,则将新任务添加到根节点,同时检查新添加的Task是否在根节点中有潜在的子节点(因为那些孤立的节点)在之前添加到根节点。

因此,如果您实际插入所有任务以实现依赖关系,那么最终会得到一个完整且有效的树。 如果您没有提供所有父节点,则最终会使一些分支完整且有效,并在根节点中有一堆孤立的分支。 但这没有问题,因为有一个简单的技巧可以区分完整的分支和孤儿:只需检查ppid是否等于根节点的pid和voila,你只输出那些完整的分支。 / p>