binary-tree-practice2

::: info Prerequisite Knowledge

Before reading this article, you should first learn:

:::

This article follows Binary Tree Key Ideas (Overview)arrow-up-right. Let's review the main points for solving binary tree problems:

::: note Note

There are two main ways to solve binary tree problems:

1. Can you get the answer by traversing the binary tree once? If yes, use a traverse function with external variables. This is called the "traversal" approach.

2. Can you define a recursive function that uses the answers of subproblems (subtrees) to get the answer of the original problem? If yes, write this recursive function and use its return value. This is the "divide and conquer" approach.

No matter which way you use, you should think about:

If you take out a single tree node, what should it do? When should it do it (preorder, inorder, postorder)? You don't need to worry about other nodes. The recursive function will handle all nodes for you.

:::

The first article Binary Tree Key Ideas (Thinking)arrow-up-right talked about the "traversal" and "divide and conquer" approaches. This article explains construction problems for binary trees.

Most binary tree construction problems use the "divide and conquer" idea: build the whole tree = root node + build left subtree + build right subtree.

Let's look at some problems.

Construct Maximum Binary Tree

Let's start with an easy one. This is LeetCode 654 "Maximum Binary Treearrow-up-right". Here is the problem:

LeetCode 654. Maximum Binary Tree

You are given an integer array nums with no duplicates. A maximum binary tree can be built recursively from nums using the following algorithm:

  • Create a root node whose value is the maximum value in nums.

  • Recursively build the left subtree on the subarray prefix to the left of the maximum value.

  • Recursively build the right subtree on the subarray suffix to the right of the maximum value.

Return *the maximum binary tree built from *nums.

Example 1:**

Example 2:**

Constraints:

  • 1 <= nums.length <= 1000

  • 0 <= nums[i] <= 1000

  • All integers in nums are unique.

Each binary tree node can be seen as the root of a subtree. For the root, we first need to build the root itself, then build its left and right subtrees.

So, we scan the array to find the maximum value maxVal. This becomes the root node root. Then, we recursively build the left subtree with the subarray to the left of maxVal, and the right subtree with the subarray to the right.

For example, if the input is [3,2,1,6,0,5], the root does this:

The maximum value in nums is the root. Then, recursively build the left and right subtrees using the left and right parts of the array.

With this idea, let's write a helper function build to control the indices:

That's it for this problem. It's pretty simple. Now let's look at two harder problems.

Build a Binary Tree from Preorder and Inorder Traversal

LeetCode problem 105 “Construct Binary Tree from Preorder and Inorder Traversalarrow-up-right” is a classic question often asked in interviews and written tests:

LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal

Given two integer arrays preorder and inorder where preorder is the preorder traversal of a binary tree and inorder is the inorder traversal of the same tree, construct and return the binary tree.

Example 1:**

Example 2:**

Constraints:

  • 1 <= preorder.length <= 3000

  • inorder.length == preorder.length

  • -3000 <= preorder[i], inorder[i] <= 3000

  • preorder and inorder consist of unique values.

  • Each value of inorder also appears in preorder.

  • preorder is guaranteed to be the preorder traversal of the tree.

  • inorder is guaranteed to be the inorder traversal of the tree.

Let’s get straight to the main idea. First, think about what the root node should do.

Like the previous question, we need to find the value of the root node, build the root, and then use recursion to build the left and right subtrees.

First, let’s review the characteristics of preorder and inorder traversal results.

In the article Binary Tree Frameworksarrow-up-right, we discussed how different traversal orders affect the arrangement of elements in the preorder and inorder arrays:

It’s easy to find the root node; the first value in preorder, preorder[0], is the root.

The key is how to use the root value to split the preorder and inorder arrays and build the left and right subtrees.

In other words, what should be put in the ? parts of the following code:

In the code, the variables rootVal and index correspond to this situation:

Some readers may notice that using a for loop to find index is not very efficient. It can be improved.

Since the values in the tree are unique, we can use a HashMap to map values to their indexes. Then we can quickly find the index for rootVal:

Now, let’s look at the diagram and fill in the blanks. What should be filled in for these question marks:

The start and end indexes for the left and right subtrees in the inorder array are easy to figure out:

What about the preorder array? How do we find the start and end indexes for the left and right subtrees?

We can figure this out by counting the number of nodes in the left subtree. Let’s call this number leftSize. Here is how the indexes look in the preorder array:

Looking at the diagram, we can write the indexes for the preorder array:

Now, the whole algorithm is complete. We just need to add the base case to finish the solution:

The main function only needs to call the buildTree function. The solution may look long and the function has many parameters, but all these parameters do is control the range in the arrays. Drawing a diagram makes everything clear.

Build a Binary Tree from Postorder and Inorder Traversal

This problem is similar to the previous one. This time, we use the postorder and inorder traversal arrays to build a binary tree. This is LeetCode Problem 106: Construct Binary Tree from Inorder and Postorder Traversalarrow-up-right:

LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal

Given two integer arrays inorder and postorder where inorder is the inorder traversal of a binary tree and postorder is the postorder traversal of the same tree, construct and return the binary tree.

Example 1:**

Example 2:**

Constraints:

  • 1 <= inorder.length <= 3000

  • postorder.length == inorder.length

  • -3000 <= inorder[i], postorder[i] <= 3000

  • inorder and postorder consist of unique values.

  • Each value of postorder also appears in inorder.

  • inorder is guaranteed to be the inorder traversal of the tree.

  • postorder is guaranteed to be the postorder traversal of the tree.

Let's look at the features of postorder and inorder traversals:

Because of the difference in traversal orders, the elements in the postorder and inorder arrays have the following layout:

The key difference from the previous problem is that postorder is the opposite of preorder, so the root node is the last element in the postorder array.

The overall algorithm structure is very similar to the previous problem. We still write a helper function called build:

Now, the status of postorder and inorder is as follows:

We can fill in the missing indices as shown above:

Here is the complete solution code:

With the foundation from the previous problem, this one is easy to solve. The only difference is that rootVal is now the last element, and we need to adjust the function parameters a bit. As long as you understand the features of a binary tree, this problem is not hard to write.

Constructing a Binary Tree from Preorder and Postorder Traversal

This is LeetCode Problem 889: Construct Binary Tree from Preorder and Postorder Traversalarrow-up-right. You are given the preorder and postorder traversal of a binary tree. Your task is to restore the structure of the binary tree.

The function signature is:

This problem is different from the previous two:

Given preorder and inorder, or postorder and inorder traversal, you can restore the unique original binary tree. But with preorder and postorder, you cannot restore a unique tree.

The problem also says, if there are multiple possible answers, you can return any of them.

Why is that? As we said before, the usual way to build a binary tree is simple: find the root node, then find and build the left and right subtrees recursively.

In the previous two problems, you could use preorder or postorder to find the root, then use inorder to know the left and right subtrees (the problem says all values are unique).

In this problem, you can find the root, but you cannot know exactly which nodes belong to the left or right subtrees.

For example, given this input:

Both of the following trees fit the condition, but their structures are different:

However, the logic to restore the tree from preorder and postorder is not much different from the previous problems. You still use the indexes of left and right subtrees to build the tree:

1. First, take the first element of preorder or the last element of postorder as the root.

2. Then, take the second element of preorder as the root of the left subtree.

3. In postorder, find the index of the left subtree's root, so you know the boundary for the left subtree, and then for the right subtree. Build the left and right subtrees recursively.

For details, see the code.

The code is very similar to the previous two problems. While reading the code, you can think about why the answer is not unique with preorder and postorder.

The key part is this line:

We assume the second element in preorder is the root of the left subtree. But actually, the left subtree could be empty, then this element is the root of the right subtree. Since we can't know for sure, the answer is not unique.

Now, we have solved the problem of restoring a binary tree from preorder and postorder traversal.

To sum up, constructing a binary tree usually uses the "divide the problem" idea: build the whole tree = root node + build left subtree + build right subtree. First find the root, then use the root's value to find the left and right subtree elements, then build them recursively.

Do you understand the trick now?

Last updated