Back to Trees And Trie

Maximum Binary Tree

Maximum Binary Tree

🧩 Problem Statement

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

  1. Create a root node whose value is the maximum value in nums.
  2. Recursively build the left subtree on the subarray prefix to the left of the maximum value.
  3. 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:

Input:

nums = [3,2,1,6,0,5]

Output:

[6,3,5,null,2,0,null,null,1]

Explanation:

  • Max is 6. Root is 6. Left: [3,2,1], Right: [0,5].
  • Left of 6: Max is 3. Root 3. Left: [], Right: [2,1].
  • Right of 3: Max is 2. Root 2. Left: [], Right: [1].
  • Right of 6: Max is 5. Root 5. Left: [0], Right: [].

šŸ” Approaches

1. Recursive Construction ($O(N^2)$)

  • Concept: Find max in current range, make it root, recurse left and right.
  • Time Complexity: $O(N^2)$ in worst case (sorted array), $O(N \log N)$ average.

2. Monotonic Stack ($O(N)$)

  • Concept: Iterate through numbers. Keep a stack of nodes on the "right path" from root to current leaf.
  • Algorithm:
  • For each number num:
  • Create curr = TreeNode(num).
  • Pop from stack while stack.top < curr. The last popped node becomes curr.left (it effectively finds its parent which is curr for now).
  • If stack is not empty, stack.top.right = curr (current is the right child of the closest larger number on left).
  • Push curr.
  • The bottom of the stack is the root.

ā³ Time & Space Complexity

  • Time Complexity: $O(N)$. Each node pushed and popped at most once.
  • Space Complexity: $O(N)$.

šŸš€ Code Implementations

C++

#include <vector>
#include <stack>
#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
vector<TreeNode*> stk;
for (int num : nums) {
TreeNode* curr = new TreeNode(num);
while (!stk.empty() && stk.back()->val < num) {
curr->left = stk.back();
stk.pop_back();
}
if (!stk.empty()) {
stk.back()->right = curr;
}
stk.push_back(curr);
}
return stk.front();
}
};

Python

from typing import List, Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def constructMaximumBinaryTree(self, nums: List[int]) -> Optional[TreeNode]:
stack = []
for num in nums:
curr = TreeNode(num)
while stack and stack[-1].val < num:
curr.left = stack.pop()
if stack:
stack[-1].right = curr
stack.append(curr)
return stack[0] if stack else None

Java

import java.util.*;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
Deque<TreeNode> stack = new ArrayDeque<>();
for (int num : nums) {
TreeNode curr = new TreeNode(num);
while (!stack.isEmpty() && stack.peek().val < num) {
curr.left = stack.pop();
}
if (!stack.isEmpty()) {
stack.peek().right = curr;
}
stack.push(curr);
}
return stack.isEmpty() ? null : stack.peekLast();
}
}

šŸŒ Real-World Analogy

Building a Pyramid:

You place blocks. If a bigger block comes, it crushes everything smaller before it (makes them its left children). If it's smaller, it sits on the right shoulder of the last block.

šŸŽÆ Summary

āœ… Cartesian Tree: This is essentially constructing a Cartesian tree from an array.

Solution Code
O(n) TimeO(1) Space
#include <iostream>
#include <queue>
#include <stack>
#include <vector>

using namespace std;

struct TreeNode {
  int val;
  TreeNode *left;
  TreeNode *right;
  TreeNode() : val(0), left(nullptr), right(nullptr) {}
  TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  TreeNode(int x, TreeNode *left, TreeNode *right)
      : val(x), left(left), right(right) {}
};

class Solution {
public:
  TreeNode *constructMaximumBinaryTree(vector<int> &nums) {
    vector<TreeNode *> stk;
    for (int num : nums) {
      TreeNode *curr = new TreeNode(num);
      while (!stk.empty() && stk.back()->val < num) {
        curr->left = stk.back();
        stk.pop_back();
      }
      if (!stk.empty()) {
        stk.back()->right = curr;
      }
      stk.push_back(curr);
    }
    return stk.front();
  }
};

void levelOrder(TreeNode *root) {
  if (!root)
    return;
  queue<TreeNode *> q;
  q.push(root);
  cout << "[";
  while (!q.empty()) {
    TreeNode *curr = q.front();
    q.pop();
    if (curr) {
      cout << curr->val << ",";
      q.push(curr->left);
      q.push(curr->right);
    } else {
      cout << "null,";
    }
  }
  cout << "]" << endl;
}

int main() {
  Solution sol;
  vector<int> nums = {3, 2, 1, 6, 0, 5};
  TreeNode *root = sol.constructMaximumBinaryTree(nums);
  cout << "Test Case 1: ";
  // Output should represent [6,3,5,null,2,0,null,null,1]
  // Just identifying the root is 6 and left is 3 and right is 5 is good enough
  // for basic verify
  if (root->val == 6 && root->left->val == 3 && root->right->val == 5) {
    cout << "Verified (Root: 6, Left: 3, Right: 5)" << endl;
  } else {
    cout << "Failed" << endl;
  }
  return 0;
}
SYSTEM STABLE
UTF-8 • STATIC_RENDER