我应该使用什么样的数据结构来实现UPGMA?

时间:2018-05-18 07:05:38

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

由于我不知道如何提出这个问题或者我的头衔会更好,我会先进道歉。

我正在尝试实施UPGMA Algorithm from Wikipedia。说我有一个向量的矢量向量。

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

整数表示程序中的特定字符串。现在,让我说我有特定的输入告诉我将test[0]test[3]合并在一起,一旦合并,我们将合并的向量推回到最后,我们删除test[0]和{ {1}}看起来像这样:

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<std:vector<int>>将失败。随着时间的推移,这将变得更糟。因为我可能有这样的东西:

merge = {test[x][0],test[y][0]}

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

我再次为这个糟糕的问题道歉。

2 个答案:

答案 0 :(得分:1)

你在这里建造一棵树。最好通过在这里创建新节点来实现。因此,当您合并{0}和{3}时,将创建一个值为{0,3}的新节点。如果然后合并{2},则创建一个节点{2,0,3}。

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

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

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

答案 1 :(得分: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);
    }
}