I'm currently trying to come up with an algorithm to implement full binary trees (binary trees with either two or no children) dynamically. The code will be implemented in C++. I can implement my trees by hand, but as I have about 900 different trees I need to do this automatically. And here I need some ideas, hints and help.
Let's start to explain what I'm doing: I get my trees from a graph merging process. I merge two nodes in a graph based on a cost function. The label of the bigger node will be the label of the merged node (my graphs nodes have a spatial size). So I end up with a vector which contains the merged nodes and the remaining label for the new node (Note that the remaining label is based on the node size and not related to the note number). The last two entries in the vector are the first nodes which merge together. The next higher entry is the remaining label for the merged node. This continues in this order until just the root node (first entry) is left.
The first problem is, my graphs have a different amount of nodes. So my trees from the merging process have different sizes, ie my vector has a different size.
To give you an idea what this looks like, see the following example:
treelist vector (the numbers on the left side, so a one dimensional vector):
27| root (0)--> root (27 and 33 are merged, remaining label is 27)
33| right (1)
27| left (2)
27| subroot (3)--> t1 (3 and 27 are merged, remaining label is 27)
27| right (4)
3 | left (5)
27| subroot (6) --> t2 (10 and 27 are merged, remaining label is 27)
27| right (7)
10| left (8)
27| subroot (9) --> t3 (17 and 27 are merged, remaining label is 27)
27| right (10)
17| left (11)
33| subroot (12)--> t4 (31 and 33 are merged, remaining label is 33)
33| right (13)
31| left (14)
Tree build by the above treelist vector in ASCII format:
root
27
/ \
27(t1) 33(t4)
/ \ / \
3 27(t2) 31 33
/ \
10 27(t3)
/ \
17 27
Again, the actual vector I have is just the numbers on the left side. The words left/right refers to the side of which its connected to the root/subroot. The number in the brackets are the relative position in the vector.
There are two rules which my trees are following:
What I want to do now is to create a general algorithm to build trees like the above with different sizes (up to about 15-20). Size refers here to the amount of internal nodes, for the tree above it would be 4.
My idea is/was to create these trees by a switch-case for the given treesize. Unfortunately, I end up with the a lot of if-statements within these cases and for higher cases its almost not managable anymore.
See my pseudo algorithm for the general case below (trees are build bottom up):
PseudoCode: (Note that for building the tree the first insert is the left node and the second is the right node)
switch(treesize)
case 3: root = vector[0]; //represents root node
t1 = vector[3]; //represents t1
t2 = vector[6]; //represents t2
t3 = vector[9]; //represents t3
// create node t3
t3->insert(vector[11]); t3->insert(vector[10]);
// create node t2
if (t3 == vector[8]) {t2->addChild(t3); t2->insert(vector[7]);}
if (t3 == vector[7]) {t2->insert(vector[8]); t2->addChild(t3);}
else {t2->insert(vector[8]); t2->insert(vector[7]);}
// create node t1
if (t3 == vector[5] && t2 != vector[4]) {t1->addChild(t3); t2->insert(vector[4]);}
if (t3 == vector[4] && t2 != vector[5]) {t1->insert(vector[5]); t1->addChild(t3);}
if (t2 == vector[5] && t3 != vector[4]) {t1->addChild(t2); t1->insert(vector[4]);}
if (t2 == vector[4] && t3 != vector[5]) {t1->insert(vector[5]); t1->addChild(t2);}
if (t3 == vector[5] && t2 == vector[4]) { t1->addChild(t3); t1->addChild(t2);}
if (t2 == vector[5] && t3 == vector[4]) { t1->addChild(t2); t1->addChild(t3);}
else {t1->insert(vector[5]); t1->insert(vector[4]);}
// create root (Note that t1 is always connected to the root)
if (t3 == vector[2]) {root->addChild(t3); root->addChild(t1);}
if (t2 == vector[2]) {root->addChild(t2); root->addChild(t1);}
if (t3 == vector[1]) {root->addChild(t1); root->addChild(t3);}
if (t2 == vector[1]) {root->addChild(t1); root->addChild(t2);}
if (t1 == vector[2] && (t3 && t2) != vector[1]) {root->addChild(t1); root->insert(vector[1]);
if (t1 == vector[1] && (t3 && t2) != vector[2]) {root->insert(vector[2]); root->addChild(t1);}
case 4: root = vector[0]; //represents the root node
t1 = vector[3]; //represents t1
t2 = vector[6]; //represents t2
t3 = vector[9]; //represents t3
t4 = vector[12]; //represents t4
// create t4
t4->insert(vector[14]); t3->insert(vector[13]);
// create t3
if (t4 == vector[11]) {t3->addChild(t4); t3->insert(vector[10]);}
if (t4 == vector[10]) {t3->insert(vector[11]); t3->addChild(t4);}
else {t3->insert(vector[11]); t3->insert(vector[10]);}
// create t2
if (t4 == vector[8] && t3 != vector[7]) {t2->addChild(t4); t2->insert(vector[7]);}
if (t4 == vector[7] && t3 != vector[8]) {t2->insert(vector[8]); t2->addChild(t4);}
if (t3 == vector[8] && t4 != vector[7]) {t2->addChild(t3); t2->insert(vector[7]);}
if (t3 == vector[7] && t4 != vector[8]) {t2->insert(vector[8]); t2->addChild(t3);}
if (t4 == vector[8] && t3 == vector[7]) {t2->addChild(t4); t2->addChild(t3);}
if (t3 == vector[8] && t4 == vector[7]) {t2->addChild(t3); t2->addChild(t4);}
else {t2->insert(vector[8]); t2->insert(vector[7]);}
// create t1
if (t4 == vector[5] && t3 != vector[4] && t2 != vector[4]) {t1->addChild(t4); t1->insert(vector[4]);}
if (t4 == vector[4] && t3 != vector[5] && t2 != vector[5]) {t1->insert(vector[5]); t1->addChild(t4);}
if (t3 == vector[5] && t4 != vector[4] && t2 != vector[4]) {t1->addChild(t3); t1->insert(vector[4]);}
if (t3 == vector[4] && t4 != vector[5] && t2 != vector[5]) {t1->insert(vector[5]); t1->addChild(t3);}
if (t2 == vector[5] && t4 != vector[4] && t3 != vector[4]) {t1->addChild(t5); t1->insert(vector[4]);}
if (t2 == vector[4] && t4 != vector[5] && t3 != vector[5]) {t1->insert(vector[5]); t1->addChild(t2);}
if (t4 == vector[5] && t3 == vector[4]) {t1->addChild(t4); t1->addChild(t3);}
if (t4 == vector[5] && t2 == vector[4]) {t1->addChild(t4); t1->addChild(t2);}
if (t3 == vector[5] && t2 == vector[4]) {t1->addChild(t3); t1->addChild(t2);}
if (t3 == vector[5] && t4 == vector[4]) {t1->addChild(t3); t1->addChild(t4);}
if (t2 == vector[5] && t3 == vector[4]) {t1->addChild(t2); t1->addChild(t3);}
if (t2 == vector[5] && t4 == vector[4]) {t1->addChild(t2); t1->addChild(t4);}
// create root (Note that t1 is always connected to the root)
if (t4 == vector[2]) {root->addChild(t4); root->addChild(t1);}
if (t3 == vector[2]) {root->addChild(t3); root->addChild(t1);}
if (t2 == vector[2]) {root->addChild(t2); root->addChild(t1);}
if (t4 == vector[1]) {root->addChild(t1); root->addChild(t4);}
if (t3 == vector[1]) {root->addChild(t1); root->addChild(t3);}
if (t2 == vector[1]) {root->addChild(t1); root->addChild(t2);}
if (t1 == vector[2] && (t4 && t3 && t2) != vector[1]) {root->addChild(t1); root->insert(vector[1]);}
if (t1 == vector[1] && (t4 && t3 && t2) != vector[2]) {root->insert(vector[2]); root->addChild(t1);}
case 5: .....
....
....
As you notice this will be a mess for higher cases.
So I always have to check if the left or right node is maybe a connection to one of the subroots. This actually causes all these if-statements.
My question now is: Do you have any idea how to implement this algorithm in a better fashion or even totally different? Any advice on this is helpful.
Thank you very much in advance for any help.
Let me know if something is not clear or if I should give a second example.
Graph:
My cost function to merge two nodes is C = Node_A + Node_B / Edgeweight.
Merging process:
1. 12+6 -> 6
2. 24+31 -> 24
3. 6+24 -> 6
4. 6+9 -> 6
5. 6+22 -> 6
Resulting Vector (same properties as above) and Tree:
(Note that the first two nodes which merges are the last two entries)
6 --> root root
22 6
6 / \
6 --> t1 6(t1) 22
9 / \
6 6(t2) 9
6 --> t2 / \
24 6(t4) 24(t3)
6 / \ / \
24 --> t3 6 12 24 31
31
24
6 --> t4
12
6
Maybe there is also a more elegenat way to build my vector from the merging process to obtain my trees in the next step.
I finally solved my problem (using C++) and want to present my solution here, so that anyone with a similar problem can get an idea. Please note that this is not necessarily the best solution, but it works well for me:
First I created a node structure, called snode:
struct snode {
unsigned int label;
std::deque<snode*> children;
snode(const unsigned int& l) //! allocates leaf with label l.
: label(l), children() { }
snode(const snode& n)
: label(n.label), children()
{ for (const auto& c : n.children)
children.push_back(new snode(*c)); }
~snode()
{ for (const auto& c : children) delete c; }
const std::deque<snode*>& getChildren() const //! returns children of this node.
{ return children; }
unsigned int getLabel() const //! returns label of this node.
{ return label; }
bool isLeaf() const //! returns true if node is a leaf.
{ return children.empty(); }
snode& insert(const snode& n, const bool before = false) //! adds child n at the beginning or end of list.
{ if (before) children.push_front(new snode(n));
else children.push_back(new snode(n));
return *this; }
};
In the next step, I read in my vector to create my treelist, which is the merging vector explained in my original question. Also, I read in the leaves of my tree, ie the original nodes before the merging process:
// create a vector to save merging list
static uvec treeList()
{ FILE* fp = openFile("mergedlist", "r");
uvec treelist; unsigned int i = 0;
while ((fscanf(fp, "%d", &i)) != EOF) {
treelist.push_back(i); }
closeFile(fp);
return treelist;
}
// create a vector to save basin list
static uvec leafList()
{ FILE* fpp = openFile("leaflist", "r");
uvec leaflist; unsigned int i = 0;
while ((fscanf(fpp, "%d", &i)) != EOF) {
leaflist.push_back(i); }
closeFile(fpp);
return leaflist;
}
Now the I developed a function to let my trees grow dynamically:
static snode* growTree(const uvec& treelist, const uvec& leaflist)
{ std::map<unsigned int,snode*> vn;
for (unsigned int i = 0; i < leaflist.size(); i++) { // Loop over all leaves
unsigned int leaf = leaflist[i];
vn[leaf] = new snode(leaf); } // save leaf nodes and allocate snode
for (unsigned int j = 0; j < treelist.size();) { // Loop through all treelist elements
unsigned int mNode = treelist[j]; // save the current merged node (for j = 0 it's the root, j = 3 is t1...)
unsigned int rNode = treelist[j+1]; // save the right child of the merged node
unsigned int lNode = treelist[j+2]; // save the left child of the merged node
snode m(mNode);
m.insert(*vn[lNode]); m.insert(*vn[rNode]);
delete vn[rNode];
delete vn[lNode]; vn[lNode] = new snode(m);
j = j+3; } // counting j+3 to access the next merged node in the treelist vector
const auto root = vn.begin();
return root->second;
}
Thanks to everybody who commented to my original post.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.