Interview Summary-tree

Source: Internet
Author: User


Tree

Tree-based questions are basically binary Trees. However, if binary is not mentioned during the interview, do not prioritize them first. It may be multi-forks (this is also a trap. When your thinking is similar, the interviewer said: I didn't say it was a binary tree, and then you would be black.) Don't add conditions for yourself. The tree has the following questions:

I. Three types of tree traversal. For pre-order, middle-order, and post-order, if you test traversal directly, it must be for you to write non-recursive Code (the recursive version is too retarded). The specific writing method should not be noted down, if you do not want to refer to the "recursion" section, how to recursively convert it to non-recursion. The other is to give a middle order + A pre-order (post-order), so that you can restore a binary tree. The middle order must be given, otherwise, it cannot be restored (the solution is not unique). It is generally recursive;

Ii. BST (Binary Search Tree ). This method is more accurate. How can we determine whether it is a BST (or a balance tree, or a full tree), and convert an ordered array (an ordered linked list) to a BST, find the maximum value of an upper_bound in BST (this allows the root user to find the node that meets the requirements, a node that is equal to upper_bound, and a parent pointer for you to find ), there are other

Iii. LCA (Least Common Ancestor, recent Common Ancestor ). Super High Frequency questions, the main test method is to give two pointers and the root of the tree, find the LCA, if the node has a parent node (this time does not give root), it is equivalent to the chain table to find the first intersection, A little trouble if there is no parent;

Iv. serialization and sending serialization. This test method is relatively simple, that is, to write a serialization and serialization method. If you have thought about it, it will take seconds. The same problem is the serialization of string arrays. The general idea is to add a head that records segment information or add a non-existent character as a segmentation. Sometimes it can be said that any character may appear. At this time, escape characters can be used (think about how to record the C string ).

 

 


1. tree traversal.

The test is a non-recursive program. The pre-order and Middle-order are better adapted, and the post-order is a little more difficult. Manually simulate the stack and study it. (refer to the recursive iteration I wrote earlier: interview Summary-recursive algorithm analysis ). Code for reference:


[Cpp] struct Node {
TreeNode * t_node _;
Bool sign; // record whether the node has been accessed
Node (TreeNode * n ){
T_node _ = n;
Sign = false;
}
};
 
Void PostOrder (TreeNode * root ){
Stack <Node> stk;
Stk. push (Node (root, false ));
While (! Stk. empty ()){
Node tmp = stk. top ();
Stk. top (). sign = true; // once taken from the top of the stack, set it to true
If (tmp. sign ){
Visit (tmp. t_node _);
Stk. pop ();
} Else {
If (tmp. t_node _-> right)
Stk. push (tmp. t_node _-> right );
If (tmp. t_node _-> left)
Stk. push (tmp. t_node _-> left );
}
}
}

Struct Node {
TreeNode * t_node _;
Bool sign; // record whether the node has been accessed
Node (TreeNode * n ){
T_node _ = n;
Sign = false;
}
};

Void PostOrder (TreeNode * root ){
Stack <Node> stk;
Stk. push (Node (root, false ));
While (! Stk. empty ()){
Node tmp = stk. top ();
Stk. top (). sign = true; // once taken from the top of the stack, set it to true
If (tmp. sign ){
Visit (tmp. t_node _);
Stk. pop ();
} Else {
If (tmp. t_node _-> right)
Stk. push (tmp. t_node _-> right );
If (tmp. t_node _-> left)
Stk. push (tmp. t_node _-> left );
}
}
}
In fact, bool sign is not recorded in struct, And the last accessed node pre is recorded during iteration. If pre = top. right or top. right = NULL, pop can be used, which saves space, but this method is not applicable everywhere. In order to unify various non-recursive methods, I recorded the information in the Node. You can write other traversal data by yourself. For the question of restoring a binary tree by means of a Forward (backward) traversal and a forward (forward) traversal, The leetcode can be used to summarize the tree code.

 

Ii. BST (Binary Search Tree ).

The BST questions mentioned above are not very difficult to write. I would like to talk about one of the high-frequency questions: converting an ordered linked list to a BST. One way is to find the midpoint in a traversal table, use the midpoint as the root, and recursively process the left and right sides. In this way, the time-space complexity is O (nlogn) + O (1 ), another way is to store all the pointers to the linked list in the vector, which is converted into an ordered array to the BST. With Random subscript access, you can O (1) the time point is found, and then the left and right parts are recursively processed. The time-space complexity is O (n) + O (n ). This code is also included in the Code Summary section.

 

Iii. LCA (Least Common Ancestor, recent Common Ancestor ).

It seems that the method for searching for the LCA after the preprocessing O (1) is generally not used. It is generally an online algorithm. It is a good idea for parent, that is, to find the first intersection node of the two linked lists: Do not traverse the first linked list and do hash, when traversing the second linked list, find the first node in the hash as the desired node. O (h) + O (h) (h is the height of the tree ); if you want to run two linked lists separately and assume that there are a and B (a> = B) nodes respectively, the first linked list goes first step a-B, and then the two linked lists go together, each step is taken to determine whether the two nodes are the same. The first same, time-space complexity O (h) + O (1) is returned. However, this requires two linked lists.

The common interface that does not provide parent is TreeNode * LCA (TreeNode * root, TreeNode * p1, TreeNode * p2 ). The general practice is to get an integer parameter in the function parameter and record the subtree with the current node as the root, which contains several of p1 and p2.

Code:


[Cpp] node * lca (node * root, node * p1, node * p2, int & num ){
If (! Root) {num = 0; return NULL ;}
Int leftnum = 0, rightnum = 0;
Node * left = lca (root-> left, p1, p2, leftnum); // return directly if left subtree is found
If (leftnum = 2) return left;
Node * right = lca (root-> right, p1, p2, rightnum); // right subtree
If (rightnum = 2) return right;
Num = left + right;
If (p1 = root) num ++;
If (p2 = root) num ++;
If (num = 2) return root; // The current tree cannot be computed.
Return NULL; // NULL is returned if none of them are found.
}

Node * lca (node * root, node * p1, node * p2, int & num ){
If (! Root) {num = 0; return NULL ;}
Int leftnum = 0, rightnum = 0;
Node * left = lca (root-> left, p1, p2, leftnum); // return directly if left subtree is found
If (leftnum = 2) return left;
Node * right = lca (root-> right, p1, p2, rightnum); // right subtree
If (rightnum = 2) return right;
Num = left + right;
If (p1 = root) num ++;
If (p2 = root) num ++;
If (num = 2) return root; // The current tree cannot be computed.
Return NULL; // NULL is returned if none of them are found.
}
This part is a solution to a question and does not summarize any commonalities.

 

Iv. serialization and sending serialization.

For a serialization method, refer to this: serialize. If the value of a node is a string that can contain any character, use the escape character. Since the tree is serialized, the serialization of the Tree node cannot be avoided. For example, if the value of the Fruit Tree node is a string array, the sub-Problem of tree serialization is the string serialization problem, nothing else ~ Continue to use escape characters.

 

Note that when processing a tree recursively, there are usually two types of recursive return conditions:


[Cpp] (1) if (! Node) return;
(2) if (! Node-> left &&! Node-> right) return;

(1) if (! Node) return;
(2) if (! Node-> left &&! Node-> right) return;
The two differences are that the first one will return back when the non-leaf node has a right son and no left son. For example, when the current node has a right son and no left son, there will be a return after entering the left son. Of course, if you add an if (root-> left) Recursive () before recursion, it will not enter this NULL node, but I will not add it for simplicity ); second, only leaf nodes are returned. There is a difference between the two. The difference lies in whether the solution of the question must appear on the leaf node. For example, you can find the shortest distance from the root node to the leaf node, generally, it is more appropriate to use the second judgment condition. Let's write your own experiences.

In addition, if you want to recursively judge a son (if (root-> left), you do not know whether the child node has been entered, I don't know if the variables you need to update on this son node have been updated. At this time, you need to be careful when initializing these variables.

Let's explain the meaning of this passage to an example, for example, the following code:


[Cpp] int Max (TreeNode * root ){
Int left, right;
If (! Root-> left &&! Root-> right)
Return root-> val;
If (root-> left) left = Max (root-> left );
If (root-> right) right = Max (root-> right );
Return max (root-> val, max (left, right ));
}

Int Max (TreeNode * root ){
Int left, right;
If (! Root-> left &&! Root-> right)
Return root-> val;
If (root-> left) left = Max (root-> left );
If (root-> right) right = Max (root-> right );
Return max (root-> val, max (left, right ));
} This code is problematic because the values of left and right may not be initialized. you are not sure whether the program recursively processes the left and right Parts. It uses the recursive return condition (2). If you want to use this condition, you need to initialize left and right, for example:

[Cpp] left = right = MIN_INT;

Left = right = MIN_INT; if the return condition (1) is used, it can be written:


[Cpp] int Max (TreeNode * root ){
If (! Root) return MIN_INT;
Return max (root-> val, max (Max (root-> left), Max (root-> right )));
}

Int Max (TreeNode * root ){
If (! Root) return MIN_INT;
Return max (root-> val, max (Max (root-> left), Max (root-> right )));
}
Relatively speaking, using (1) is simpler. The reason for not using (2) is that the solution does not have to appear on the leaf node. Because if (! Root-> left &&! Root-> right) This condition is used to determine whether the root is a leaf. In this example, the Max function does not necessarily require that the value be a leaf.

 


To put it simply, use (1) as much as possible (1) (for example, the above LCA can also be changed to (2 ), however, it is impossible to end at the leaf node. In this case, if (root-> child) needs to be judged during recursion. if this function has a variable to return, for example, if a depth is used, pay attention to the value of this variable, because you may not enter this recursion. If depth has not been initialized, it has not entered recursion, then, depth may be an unpredictable value. If you need to use depth later, you can also give depth a value. For example, depth returns the maximum depth, and you can initialize depth to INT_MIN.

 

For recursive tree processing, first consider whether to use (1) or (2) as the termination condition (mainly to determine whether it can only be terminated on the leaf). If (2) remember to judge whether the son is NULL in recursion, and then the variable needs to be initialized.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.