Interviewer: Not even such a simple binary tree algorithm?

It's been a long time since I published an article on algorithms. Today we will look at an interesting algorithm question, which is also a high-frequency interview question.

This question is question 572 of leetcode. The requirement is this: Given two binary trees A and B, determine whether B is a subtree of A.

In the example below you can see that B is a subtree of A.

Think about how to solve this problem?

If B is a subtree of A, then B must be exactly the same as a subtree of A, so we can implement a function isSame to judge whether two binary trees are exactly the same. This function is very easy to implement:

 
 

bool isSame(TreeNode* a, TreeNode* b) { if (a == nullptr && b == nullptr) return true; else if (a == nullptr || b == nullptr) return false; else return a->val == b->val && isSame(a->left, b->left) && isSame(a->right, b->right);}

It only takes three lines of code to get it done, the function is very simple:

  1. If the binary trees a and b are both empty, then obviously return true

  2. Otherwise, if a is empty or b is empty, then the two trees are obviously not the same, return false

  3. If conditions 1 and 2 are not satisfied, then if the values ​​of the root nodes of a and b are the same and their left and right subtrees are the same, then the binary trees a and b are the same binary tree, return true

With the isSame function, the rest is simple. We only need to continuously call the isSame function when traversing the binary tree a to determine whether b is the same subtree of a:

Similarly, it only takes three lines of code to get it done:

 
 

bool isSubtree(TreeNode* root, TreeNode* subRoot) { if (root == nullptr && subRoot == nullptr) return true; else if (root == nullptr || subRoot == nullptr) return false; else return isSame(root, subRoot) || isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);}

The code is very simple, it is the ordinary traversal of the binary tree.

Some students may have discovered that this algorithm is actually not very efficient. The reason is that we need to call the isSame function for each node on the binary tree a. If the number of nodes in the binary tree a is M, and the number of nodes in the binary tree b is The number is N, then the time complexity of the algorithm is O(M*N).

Do we have to call the isSame function for each node in the binary tree a? Actually this is not necessary.

To be able to come up with more efficient algorithms, you need to understand the concept of encoding .

Students who are familiar with md5 know that we can calculate the md5 value for any file. md5 is a string of numbers, just like fingerprints. Only two files are exactly the same, so the md5 of these two files is exactly the same, so we You can confirm whether the two files are exactly the same by comparing md5. In Linux, use the md5sum command to calculate the md5 value of a file:

 
 

$ md5sum a.c6004b6a21b274b405a2bd1f1c75a93c7 a.c

Similarly, we can also calculate the "md5" value for a binary tree.

How to calculate it? It's actually pretty simple.

We only need to output the "traversal track" during the pre-order traversal of the binary tree, then a binary tree can be serialized into a string .

If the binary tree b is a subtree of a, then the serialized string of the binary tree b must be a substring of the serialized string of a.

In this way, through encoding, we transform the judging problem of binary tree subtrees into the problem of substring matching of strings , and the problem of string matching can be solved by the classic KMP algorithm.

The function to serialize a binary tree to a string is also straightforward:

 
 

void serialize(TreeNode* root, string& str) { if (root == nullptr) { str = str + "#"; } else { str = str + "," + to_string(root->val);
serialize(root->left, str); serialize(root->right, str); }}

As you can see, the code is actually a binary tree traversal.

Now that we can serialize the binary tree to a string, the next step is simple:

 
 

bool isSubtree(TreeNode* root, TreeNode* subRoot) { string a, b; serialize(root, a); serialize(subRoot, b); return a.find(b)!=string::npos;}

After serializing the binary tree into a string, it is only necessary to determine whether the string b is a substring of the string a.

From this topic, we can see the important role of information coding, which is also an idea worth mastering, and sometimes it can be very effective in solving problems .

Guess you like

Origin blog.csdn.net/2301_77463738/article/details/131152139