c++treebinary-trees-expression

Construct binary tree from s-expression in c++


empty tree ::= ()
tree ::= empty tree | (w tree tree)
ex:
()
empty tree

(99(5()())(35(-5()())()))

     99
    /  \
   5   35
      /
     -5

class Node
{
public:
    int weight; // weight can be negative!
    Node *left, *right;
    Node():weight(0),left(NULL),right(NULL){}
    Node(int d):weight(d),left(NULL),right(NULL){}
};

Construct a binary tree by given condition

I get problem with construct it, my program will crush and I have no idea about why it happened, the following is my code and I print out some information for debug, take (99(5()())(35(-5()())())) as a test case, it will print out 99(5( and crush, I think maybe problem is at which I deal with ) where I return node which is NULL, but I can’t find problem with it. By the way, this tree is expected to handle HUNDREDS of nodes in each tree, and Each of the test cases contains up to TEN-THOUSAND trees, will I run out of time with this program or what should I need to do?Thank for your time

Node* MyBinaryTreeOps::constructTree(Node *root, std::string treeStr)const
{
    int idex = 1;//always look at the treeStr[1]
    Node *cur=NULL;//use to pass in recursive call
    if(treeStr[idex]!='('&&treeStr[idex]!=')'){//meet number create new node
       stringstream ss;
       while(treeStr[idex]!='('){
             ss<<treeStr[idex];
             if(treeStr.size()>1){//if size > 1 then remove the treeStr[1],to let treeStr[1] become next char in treeStr
                treeStr.erase(1,1);
             }
        }
        int num=0;
        ss>>num;
        std::cout<<num<<std::endl;//print out just for debug
        std::cout<<treeStr[idex]<<std::endl;//print out just for debug
        root = new Node(num);
     }

    if(treeStr[idex]==')'){//meet ')' return subtree constructed
      if(treeStr.size()>1){
         treeStr.erase(1,1);
      }
       return root;
    }
    if(treeStr[idex]=='('){//meet first '(' then construct left subtree
       if(treeStr.size()>1){
          treeStr.erase(1,1);
       }

       root->left = constructTree(cur,treeStr);

    }

    if(treeStr[idex]=='('){ //meet second '(' then construct right subtree
       if(treeStr.size()>1){
          treeStr.erase(1,1);
       }
       root->right = constructTree(cur,treeStr);

    }
    if(treeStr[idex]==')'){ //meet ')' return subtree constructed
       if(treeStr.size()>1){
          treeStr.erase(1,1);
       }
       return root;
    }
}

Solution

  • I've tried this problem by myself and this is the function that I've wrote.

    Steps of the algorithm:

    1. Find a part of the sequence that represents weight of current node. Convert it to int and assign to node.
    2. Slice string to remove weight, starting and ending brace.
    3. Iterate over sequence to find point between two braces that divides children nodes.
    4. Split children string into two sequences (We can slice starting tree and reuse it as sequence of one of the children nodes).
    5. If child node has weight (length of its sequence is larger than 2) then create new node and recurse algorithm.

    Additionally, here is my program with some test examples and a little bit extended Node class:

    Node* constructTree(Node* root, std::string& treeString) {
        // Find the weight of this node.
        auto weightLeft = treeString.find_first_of("(") + 1;
        auto weightRight = treeString.find_first_of("()", weightLeft);
        auto weightString = treeString.substr(weightLeft, weightRight - weightLeft);
    
        // Optional, we check if there is any weight, if there is not we leave zero
        // weight from constructor.
        // Works for something like that: ((1)(2)) -> (0(1)(2))
        if (weightString.length() > 0) {
            root->weight = std::stoi(weightString);
        }
    
        // Slice string to contain only children sequences.
        treeString.erase(0, weightRight);
        treeString.erase(treeString.length() - 1, 1);
    
        // Looking for index in string where a left child ends and a right child starts.
        // This point(index) is located where count of left braces and for braces
        // is the same and the counts are not zero.
        int splitPoint = -1;
        int leftBraces = 0, rightBraces = 0;
        for (int index = 0; index < treeString.length(); index++) {
            char c = treeString[index];
            if (c == '(') {
                ++leftBraces;
            }
            if (c == ')') {
                ++rightBraces;
            }
    
            if (leftBraces == rightBraces) {
                splitPoint = index + 1;
                break;
            }
        }
    
        // If split point has been found then it means that this node has children.
        if (splitPoint != -1) {
            auto leftChildString = treeString.substr(0, splitPoint);
            auto rightChildString = treeString.erase(0, splitPoint);
    
            // Check for length so construct will stop if there is no child.
            if (leftChildString.length() > 2) {
                root->left = new Node();
                constructTree(root->left, leftChildString);
            }
    
            if (rightChildString.length() > 2) {
                root->right = new Node();
                constructTree(root->right, rightChildString);
            }
        }
    
        return root;
    }