Showing posts with label Tree Traversal. Show all posts
Showing posts with label Tree Traversal. Show all posts

leetCode Question: Serialize and Deserialize Binary Tree

Serialize and Deserialize Binary Tree

Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment.

Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure.

For example, you may serialize the following tree

1

/ \
2 3
/ \
4 5
as "[1,2,3,null,null,4,5]", just the same as how LeetCode OJ serializes a binary tree. You do not necessarily need to follow this format, so please be creative and come up with different approaches yourself.
Note: Do not use class member/global/static variables to store states. Your serialize and deserialize algorithms should be stateless.

Analysis:

In this problem, I have implemented a differrent way of serialization (in C++ code shown below). If you would like to see the simple version which is the same way of LeetCode OJ, please check out the pyhton code below.

Let's first see the simple way of doing this. We will encode the node value level by level, and only encode the "Null" (or "None" in python) node when it is a leaf node. Since we are "searching" the tree level by level, DFS is usually a good way to do so. Here in my solution, I choose to use two queues, to store nodes in current level, and nodes in next level, respectively. For the deserialization, we could still keep two queues for the BFS, and keep track of the node we are expanding.

Secondly, I'l like to show the implementation using a different way. Although it is not a quite efficient method, it still provides good practice of binary tree and binary tree traversal. Particularly, we have a binary tree, rather than the level by level traversal, we still have three depth-first traversal: preorder, inorder, and postorder. Therefore, we could use preorder and inorder traversal to reconstruct the binary tree. Please take a look at my previous post for details. Note that in this problem, there might be duplicate values in different nodes, we have to use the indices of each node for the traversal. So, our encoding format is: "preorder string;inorder string;value string". The "preorder string" is the indices of each node in preorder order, actually, I have given the sequence from 1 to the number of nodes in this string for simplicity. The "inorder string" is the indices of each node in inorder order. The "vaule string" is the values of each node (using preorder order) in string format. All the elements in three strings are splited using ",".

Code (C++):

/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Codec {
public:
TreeNode* reconstruct(vector<int>& in, vector<int>& pre, int st, int ed, int& idx, vector<int>& val){
if (idx!=pre.size()){
TreeNode* node = new TreeNode(val[pre[idx]]);
int i=st;
for ( ;i<ed;i++){
if (in[i]==pre[idx]){break;}
}
idx++;
if (i-1 >=st){
node->left = reconstruct(in, pre, st, i-1, idx,val);
}else{
node->left = NULL;
}
if (ed >= i+1){
node->right = reconstruct(in, pre, i+1,ed, idx,val);
}else{
node->right = NULL;
}
return node;
}else{
return NULL;
}
}
void inOrder(TreeNode* root, string& res, string& val, int& i){
if (!root){return;}
inOrder(root->left, res, val, i);
val += to_string(root->val) + ',';
root->val = i;
res += to_string(i) + ',';
i++;
inOrder(root->right,res, val, i);
}
void preOrder(TreeNode* root, string& res){
if (!root){ return; }
res += to_string(root->val) + ',';
preOrder(root->left, res);
preOrder(root->right,res);
}
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
//inorder
string in; //store the inorder tree traversal using index NOT!!! the actural value
string val; //store the actural values of each node according to the "inorder" order
int i=0;
inOrder(root, in, val, i); //inorder traversal
//preorder
string pre; //stroe the preorder tree traversal using index NOT!!! the actural value
preOrder(root, pre); //preorder traversal
//return the encoded string: inorder(index);preorder(index);values
// e.g., Tree [1,2,3,null,null,4,5] returns "0,1,2,3,4,;1,0,3,2,4,;2,1,4,3,5,"
return in + ";" + pre+ ";" + val;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
// decode the string
int pos_1 = data.find(';');
string in = data.substr(0,pos_1);
string tmp = data.substr(pos_1+1);
int pos_2 = tmp.find(';');
string pre = tmp.substr(0,pos_2);
string val = tmp.substr(pos_2+1);
vector<int> in_vec; // save the index of inorder traversal
vector<int> pre_vec; // save the index of preorder traversal
vector<int> val_vec; // save the values according to the inorder order
while (val.find(',')!=-1){
val_vec.push_back(stoi(val.substr(0,val.find(','))));
val = val.substr(val.find(',')+1);
}
while (in.find(',')!=-1){
in_vec.push_back(stoi(in.substr(0,in.find(','))));
in = in.substr(in.find(',')+1);
}
while (pre.find(',')!=-1){
pre_vec.push_back(stoi(pre.substr(0,pre.find(','))));
pre = pre.substr(pre.find(',')+1);
}
// reconstruct tree using preorder and inoreder traversal
int idx = 0;
return reconstruct(in_vec, pre_vec, 0, in_vec.size(),idx, val_vec);
}
};
// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));

Code (Python):

# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Codec:
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
q1 = []
q2 = []
res = ""
q1.append(root)
while True:
while len(q1) != 0:
tmp = q1.pop(0)
if tmp is None:
res += "null,"
else:
res += str(tmp.val) + ","
q2.append(tmp.left)
q2.append(tmp.right)
if len(q2) == 0:
break
q1 = q2[:]
q2 = []
return res
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
data_list = data[0:-1].split(',') # [0:-1] eliminates the last ','
mp1 = []
mp2 = []
head = None
if data_list[0] != "null":
mp1.append(TreeNode(int(data_list[0])))
head = mp1[0]
i = 1
while i < len(data_list):
for node in mp1:
if node is not None:
if data_list[i] != "null":
node.left = TreeNode(int(data_list[i]))
i+=1
mp2.append(node.left)
if data_list[i] != "null":
node.right = TreeNode(int(data_list[i]))
i+=1
mp2.append(node.right)
mp1 = mp2[:]
mp2 = []
return head
# Your Codec object will be instantiated and called as such:
# codec = Codec()
# codec.deserialize(codec.serialize(root))

leetcode Question: Binary Tree Right Side View

Binary Tree Right Side View

Given a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.
For example:
Given the following binary tree,
   1            <---
 /   \
2     3         <---
 \     \
  5     4       <---
You should return [1, 3, 4].

Analysis:


Note that in this problem, it is NOT to print out the right most sub tree.
It is to print out the most right element in each level.
Therefore the problem becomes pretty straightforward, which easily reminds us one of the basic tree operations:  level order traversal.

Similar to my previous post (here), we can use two queues to do the level order traversal. The only difference is in this question, we only need to store the 'last' element in each level and save it to the result vector. Implementation details can be seen in the following code.


Code(C++):

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        queue<TreeNode*> p, q;
        vector<int> res;
        if (!root) return res;
        
        p.push(root);
        while (1){
            int last;
            while (!p.empty()){
                TreeNode* cur = p.front();
                if (cur->left) q.push(cur->left);
                if (cur->right) q.push(cur->right);
                last = cur->val;
                p.pop();
            }
            res.push_back(last);
            p=q;
            while (!q.empty()){q.pop();}
            if (p.empty()) return res;
        }
    }
};

Code(Python):

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    # @param {TreeNode} root
    # @return {integer[]}
    def rightSideView(self, root):
        res = []
        if not root:
            return res
        p = []
        q = []
        p.append(root)
        while True:
            last = root.val
            while p:
                if p[0].left:
                    q.append(p[0].left)
                if p[0].right:
                    q.append(p[0].right)
                last = p[0].val
                p.pop(0);
            res.append(last)
            p = q
            q = []
            if not p:
                return res


[Re-view] Tree Traversal (PreOrder, InOrder, PostOrder): Recursion and Non-Recursion(Iteration)

[Re-view] Tree Traversal (PreOrder, InOrder, PostOrder):  Recursion and Non-Recursion(Iteration)


Introduction


In this post, I would like to talk one important algorithm---- Tree Traversal. Tree traversal is a basic but crucial concept in data structure and algorithm, which is also very hot in interviews. In this post, I assume that you have already had the idea of "what is binary tree?", "what is tree traversal?", and "what is stack?". I will focus on Recursive and Non-Recursive traversal, for PreOrder, InOrder, PostOrder, and Level Order of binary tree. Algorithm description with GIF animation are provided along with the C++ implementations. The code can be run and tested in leetcode online judge as well.

Actually the "three order" traversal is easily to memorize in this way:  Where is the root? When the root is in the first place to be visited (root->left->right), then it is "pre" order, when the root is in the middle to be visited (left->root->right), then it is "in" order, and when the root is last to be visited, it is "post" order.

PreOrder Traversal:

Description:
PreOrder traversal is according to the root, left and right order traversing the binary tree.

Recursive traversal is straightforward:  we first visit the root node, then traverse its left child, then its right child.

Non-Recursive traversal is to ulitize the data structure stack. There are three steps for this algorithm:
(1) Initialize the stack. Push the root node into stack.
(2) While the stack is not empty do:
        (a) Pop the top node from the stack.
        (b) Store the top node
        (c) Push the top node's right child into stack
        (d) Push the top node's left child into stack
(3) Output the stored sequence.

Animation(non-recursive):


Code (PreOrder Traversal):
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
      // preorder traversal: recursion 
    void pre_recur(TreeNode* root, vector<int> &res){
        if (!root){         //if null node, return
            return;
        }else{
            res.push_back(root->val); // traverse root child
            if (root->left){    
                pre_recur(root->left,res); //traverse left child
            }
            if (root->right){
                pre_recur(root->right,res); //traverse right child
            }
        }
    }
    
    // preorder traversal: non-recursion
    void pre_norecur(TreeNode* root,vector<int> &res){
        stack<TreeNode*> st;
        st.push(root);   //initialize stack
        
        TreeNode* tmp;
        while (!st.empty()){
            tmp=st.top();  //get the top node in stack 
            st.pop();
            res.push_back(tmp->val); //store the root value
            if (tmp->right){st.push(tmp->right);} //push right child into stack 
            if (tmp->left){st.push(tmp->left);} //push left child into stack 
        }
    }
    
    vector<int> preorderTraversal(TreeNode *root) {
        vector<int> res;
        if (!root){return res;}
        
        //pre_recur(root,res);   //recursive traversal 
        pre_norecur(root,res); //non-recursive traversal
        return res;
    }
};



InOrder Traversal:

Description:
InOrder traversal is according to the left, root, and right order traversing the binary tree.

Recursive traversal is straightforward:  we first traverse the left child, then visit the root node, then traverse the right child.

Non-Recursive traversal is to ulitize the data structure stack. There are three steps for this algorithm:
(1) Initialize the stack. Set root as the current node. Push the current node into stack.
(2) Loop until finished (while true):
        (a) If current has left child:
                (i)Push left child into stack.
                (ii)Current node = current node's left child 
        (b) If current has NO left child:
                (i)If stack is empty: exit
                (ii)If stack is NOT empty:
                      Pop the top node in the stack.
                      Store the top node for output.
                      Set current node = top node's right child.              
(3) Output the stored sequence.

Animation(non-recursive):
Code (InOrder Traversal):
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    // inorder traversal: recursion 
    void in_recur(TreeNode* root, vector<int> &res){
        if (!root){         //if null node, return
            return;
        }else{
            if (root->left){in_recur(root->left,res);} //traverse left child
            res.push_back(root->val); // traverse root child
            if (root->right){in_recur(root->right,res);} //traverse right child
        }
    }
    
    // inorder traversal: non-recursion
    void in_norecur(TreeNode* root,vector<int> &res){
        stack<TreeNode*> st;
        while (true){
            if (root){  // keep pushing the left child of current node
                st.push(root);
                root=root->left;
            }else{
                if (!st.empty()){   
                    TreeNode* top = st.top();   //pop the top node in the stack
                    st.pop();
                    res.push_back(top->val);    //output the top node
                    root=top->right;            //set current node=right child of top node
                }else{
                    break;  //if the stack is empty, then exit
                }
            }
        }
    }

    vector<int> inorderTraversal(TreeNode *root) {
       vector<int> res;
        if (!root){return res;}
        //in_recur(root,res);   //recursive traversal 
        in_norecur(root,res); //non-recursive traversal
        return res;
    }
};


PostOrder Traversal:

Description:
PostOrder traversal is according to the left, right, and root order traversing the binary tree.

Recursive traversal is straightforward:  we first traverse the left child, then traverse the right child, then visit the root node.

Non-Recursive traversal is to ulitize the data structure stack. Two stacks is used in this algorithm. Stack 2 is used to store the result. There are three steps:
(1) Initialize the stack. Push the root node into stack.
(2) While the stack is not empty do:
        (a) Pop the top node from the stack 1.
        (b) Push the top node into stack 2.
        (c) Push the top node's left child into stack 1.
        (d) Push the top node's right child into stack 1.
(3) Pop all the nodes in Stack 2 to get the result.

Animation(non-recursive):
Code (PostOrder Traversal):
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    // postorder traversal: recursion 
    void post_recur(TreeNode* root, vector<int> &res){
        if (!root){         //if null node, return
            return;
        }else{
            if (root->left){    
                post_recur(root->left,res); //traverse left child
            }
            if (root->right){
                post_recur(root->right,res); //traverse right child
            }
            res.push_back(root->val); // traverse root child
        }
    }
    
    // postorder traversal: non-recursion
    void post_norecur(TreeNode* root,vector<int> &res){
        stack<TreeNode*> st1;
        stack<TreeNode*> st2;
        st1.push(root);   //initialize stack one
        
        TreeNode* tmp;
        while (!st1.empty()){
            tmp=st1.top();  //get the top node in stack 1
            st1.pop();  
            st2.push(tmp);  //push into stack 2
            if (tmp->left){st1.push(tmp->left);} //push left child into stack 1
            if (tmp->right){st1.push(tmp->right);} //push right child into stack 1
        }
        
        while (!st2.empty()){  //output the stack 2
            tmp=st2.top();
            st2.pop();
            res.push_back(tmp->val);
        }
        
    }

    vector<int> postorderTraversal(TreeNode *root) {
        vector<int> res;
        if (!root){return res;}
        
        //post_recur(root,res);   //recursive traversal 
        post_norecur(root,res); //non-recursive traversal
        return res;
    }
};









leetcode Question 130: Sum Root to Leaf Numbers

Sum Root to Leaf Numbers

Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number.
An example is the root-to-leaf path 1->2->3 which represents the number 123.
Find the total sum of all root-to-leaf numbers.
For example,
    1
   / \
  2   3
The root-to-leaf path 1->2 represents the number 12.
The root-to-leaf path 1->3 represents the number 13.
Return the sum = 12 + 13 = 25.

Analysis:

Once we see this kind of problem, no matter what sum is required to output, "all root-to-leaf" phrase reminds us the classic Tree Traversal or Depth-First-Search algorithm. Then according to the specific problem, compute and store the values we need. Here in this problem, while searching deeper, add the values up (times 10 + current value), and add the sum to final result if meet the leaf node (left and right child are both NULL).

Code(C++):

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:

    void dfs(TreeNode* root,int cur, int &res){
        if (root->left==NULL && root->right==NULL){
            cur=cur*10+root->val;
            res+=cur;
        }else{
            cur=cur*10+root->val;
            if (root->left){
                dfs(root->left,cur,res);
            }
            if (root->right){
                dfs(root->right,cur,res);
            }
        }
    }
    int sumNumbers(TreeNode *root) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        int res=0;
        if (!root){return res;}
        dfs(root,0,res);
        return res;
    }
};

Code(Python):


# Definition for a  binary tree node
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    # @param root, a tree node
    # @return an integer
    res = 0
    def search(self, root, path):
        if root.left == None and root.right == None:
            self.res += path + root.val
        else:
            if root.left != None:
                self.search(root.left, (path + root.val)*10)
            if root.right != None:
                self.search(root.right, (path + root.val)*10)
            
    def sumNumbers(self, root):
        self.res = 0
        if root == None:
            return 0
        else:
            self.search(root, 0)
            return self.res