Основная проблема заключается в том, что вы рекурсивно обрабатываете для каждого узла до каждого подузла, что, как правило, сильно избыточно.Один из способов избежать этого - ввести порядок имен узлов, где «более высокие» узлы зависят только от «более низких» узлов, а затем вычислять их в обратном порядке (для каждого узла вы уже точно знаете все дочерние веса).Тем не менее, я не думаю, что есть std
алгоритмы, которые найдут этот порядок для вас, потому что вы не можете временно определить зависимости узла дешево («зависит ли узел X от узла Y? Если это не напрямую, возможно, нам придется искатьвсе дерево ... ").
Итак, вы можете просто пойти по пути динамического программирования и сохранить узлы, которые вы где-то полностью вычислили.Или даже лучше - вы могли бы просто сгладить все дерево до веса, равного только листьям, когда вы пройдете его.Пока вы сохраняете выравнивание на протяжении всей рекурсии, это действительно довольно элегантно в рекурсивной форме:
using NodeWeights = std::unordered_map<std::string, double>;
using NonLeaves = std::unordered_map<std::string, NodeWeights>;
// Modifies the tree so that the given root has no non-leaf children.
void flattenTree(std::string root, NonLeaves& toFlatten)
{
auto rootIt = toFlatten.find(root);
if (rootIt == toFlatten.end())
return;
NodeWeights& rootWeights = rootIt->second;
NodeWeights leafOnlyWeights;
for (auto kvp : rootWeights)
{
const std::string& childRoot = kvp.first;
double childWeight = kvp.second;
std::cout << "Checking child " << childRoot << std::endl;
// If the graph is indeed acyclic, then the root kvp here is untouched
// by this call (and thus references to it are not invalidated).
flattenTree(childRoot, toFlatten);
auto childIt = toFlatten.find(childRoot);
// The child is a leaf after flattening: Do not modify anything.
if (childIt == toFlatten.end())
{
leafOnlyWeights[childRoot] = childWeight;
continue;
}
// Child is still not a leaf (but all its children are now leaves):
// Redistribute its weight among our other child weights.
const NodeWeights& leafWeights = childIt->second;
for (auto leafKvp : leafWeights)
leafOnlyWeights[leafKvp.first] += childWeight * leafKvp.second;
}
rootWeights = leafOnlyWeights;
}
int main()
{
umap<std::string, umap<std::string, double>> weightTrees = {{ "Node0", {{ "Node1",0.5 },{ "Node2",0.3 },{ "Node3",0.2 }} },
{ "Node1", {{ "Node2",0.1 },{ "Node4",0.9 }} }};
auto flattenedTree = weightTrees;
flattenTree("Node0", flattenedTree);
umap<std::string, double> w = flattenedTree["Node0"]; // Should give {Node2: 0.35, Node3: 0.20, Node4: 0.45}
for (auto kvp : w)
std::cout << kvp.first << ": " << kvp.second << std::endl;
}
Демо
Поскольку каждый узел выравнивается не более одного раза, вы не можете столкнуться с экспоненциальной средой выполнения вашего исходного алгоритма.