【问题标题】:What kind of data structure should I use to implement UPGMA?我应该使用什么样的数据结构来实现 UPGMA?
【发布时间】:2018-10-28 13:24:35
【问题描述】:

我要提前道歉,因为我不知道如何问这个问题,否则我的标题会更好。

我正在尝试实现UPGMA Algorithm from Wikipedia。假设我有一个整数向量的向量。

std::vector<std::vector<int>> test = { {0},{1},{2},{3},{4},{5}}

整数代表我程序中的特定字符串。现在,假设我有特定的输入告诉我将 test[0]test[3] 合并在一起,一旦它们合并,我们将合并的向量推回最后,我们删除 test[0]test[3] 看起来像这个:

test = { {1}, {2}, {4}, {5}, {0,3} }

这很容易通过以下代码实现:

int x = 0;
int y = 3;
merge = {test[x][0],test[y][0]}; // merge is a std::vector<int>
test.push_back(merge);
test.erase(test.begin() + x)
test.erase(test.begin() + y - 1); // -1 since the first erase shifts everything over

当我想合并test[1]test[4] 时会出现问题。期望的结果如下所示:

test = { {1}, {4}, {5}, {2,{0,3} };

这是我遇到问题的地方,因为我现在似乎在我的测试的位置 3 中引入了 std::vector&lt;std:vector&lt;int&gt;&gt;。并且使用merge = {test[x][0],test[y][0]} 将失败。随着时间的推移,这种情况会变得更糟。因为我可能有一些看起来像这样的东西:

test = { {1}, {{4,5},{2,{0,3}}} }

我想我很快意识到我有错误的数据结构,但我完全不知道我需要为此使用什么数据结构。我可以使用什么样的数据结构来轻松实现这一点?

再次,我为这个不好的问题道歉。

【问题讨论】:

    标签: c++ vector data-structures c++14 c++17


    【解决方案1】:

    你正在构建一棵二叉树。每个孩子要么是int,要么是一个子树。这在 vector 的任何东西中都没有有用的建模。

    #include <vector>
    #include <variant>
    #include <memory>
    #include <utility>
    
    using Node = std::variant<std::shared_ptr<class Tree>, int>;
    
    struct Tree {
        Tree(Node left, Node right) : left(left), right(right) {}
        Node left;
        Node right;
    };
    
    std::pair<std::vector<Node>::iterator, std::vector<Node>::iterator> decide_merge(const std::vector<Node> & v)
    {
        // Some process to choose elements
        return { v.begin(), v.begin() + 1 };
    }
    
    int main()
    {
        std::vector<Node> nodes = { {0}, {1}, {2}, {3}, {4}, {5} };
        while (nodes.size() > 1)
        {
            auto [left, right] = decide_merge(nodes);
            auto tree = std::make_shared<Tree>(*left, *right);
            nodes.erase(left);
            nodes.erase(right);
            nodes.push_back(tree);
        }
    }
    

    【讨论】:

    • 我只是在检查这个,但这不能编译?我已经使用 c++17 标志运行它,但是没有定义对 decision_merge 的引用。
    • 是的。您没有描述选择要合并哪些节点的机制。
    • 它在 wiki 上被描述为 here。本质上,您根据我已经计算的距离创建一个矩阵。选择除 0 以外的最小数字,并将它们合并在一起。
    【解决方案2】:

    你正在这里建造一棵树。最好通过在此处创建新节点来做到这一点。因此,当您合并 {0} 和 {3} 时,您将创建一个具有值 {0,3} 的新节点。如果您随后合并 {2},则会创建一个节点 {2,0,3}。

    此时您可能会反对并说您需要结构 {2,{0,3}}。这实际上是不需要的,实际上是低效的。您只需要在流程结束时的结构。那时,您可以根据您没有真正删除旧节点的事实来重建树 - 您只需将它们放在一边。

    这基本上意味着您需要第二个向量向量。创建 {0,3} 后,将 {0} 和 {3} 移动到第二个向量。创建 {2,0,3} 后,将 {2} 和 {0,3} 附加到第二个向量。

    这当然不是最节省内存的实现。第二个向量的大小为 O(N*N),因为它保留了每个中间树节点。一个更节省空间的实现是用一个简单的树替换第二个向量向量,其中叶节点只有一个值,非叶节点只有两个子指针。这棵树只是保留了结构。

    【讨论】:

    • 我想我很难弄清楚如何从这些差异中创建一个矩阵。该算法很简单,我有初始差异的矩阵。但是,我不确定如何实施您的建议。这不需要知道向量 {0,3} 以某种方式存在吗?
    • @Sailanarmo:当然存在。在第 1 次迭代后,{0,3} 在第一个向量中;在迭代 2 之后,它被移动到第二个向量,因为现在第一个向量包含 {2,0,3}
    猜你喜欢
    • 1970-01-01
    • 2021-02-26
    • 2012-07-29
    • 2011-12-12
    • 1970-01-01
    • 2011-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多