由于我不知道如何提出这个问题或者我的头衔会更好,我会先进道歉。
我正在尝试实施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]}
我想我很快意识到我的数据结构错误,但我完全不知道我需要使用哪种数据结构。我可以使用什么样的数据结构来轻松实现这一点?
我再次为这个糟糕的问题道歉。
答案 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);
}
}