简体   繁体   中英

How to build a Full Binary Tree dynamically?

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:

  • t1 is always connected to the root
  • tk-1 is never a lower connection of tk, eg t1 is never the lower connection of t2

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.

See below how I get my Vector from my graph:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM