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:
Input: nums = [3,2,1,6,0,5]
Output: [6,3,5,null,2,0,null,null,1]
Explanation: The recursive calls are as follow:
- The largest value in [3,2,1,6,0,5] is 6. Left prefix is [3,2,1] and right suffix is [0,5].
- The largest value in [3,2,1] is 3. Left prefix is [] and right suffix is [2,1].
- Empty array, so no child.
- The largest value in [2,1] is 2. Left prefix is [] and right suffix is [1].
- Empty array, so no child.
- Only one element, so child is a node with value 1.
- The largest value in [0,5] is 5. Left prefix is [0] and right suffix is [].
- Only one element, so child is a node with value 0.
- Empty array, so no child.
Example 2:
Input: nums = [3,2,1]
Output: [3,null,2,null,1]
Constraints:
1 <= nums.length <= 1000
0 <= nums[i] <= 1000
All integers in nums are unique.
Solutions
Solution 1
Thinking
The root is the interval maximum; children follow the same rule. A linear scan for the max is \(O(n^2)\) worst-case, acceptable for \(n\le 10^3\).
Take max and its index, then recurse on the two sides.
1 2 3 4 5 6 7 8 910111213141516171819
# Definition for a binary tree node.# class TreeNode:# def __init__(self, val=0, left=None, right=None):# self.val = val# self.left = left# self.right = rightclassSolution:defconstructMaximumBinaryTree(self,nums:list[int])->Optional[TreeNode]:defdfs(nums):ifnotnums:returnNoneval=max(nums)i=nums.index(val)root=TreeNode(val)root.left=dfs(nums[:i])root.right=dfs(nums[i+1:])returnrootreturndfs(nums)
/** * Definition for a binary tree node. * type TreeNode struct { * Val int * Left *TreeNode * Right *TreeNode * } */funcconstructMaximumBinaryTree(nums[]int)*TreeNode{vardfsfunc(l,rint)*TreeNodedfs=func(l,rint)*TreeNode{ifl>r{returnnil}i:=lforj:=l;j<=r;j++{ifnums[i]<nums[j]{i=j}}root:=&TreeNode{Val:nums[i]}root.Left=dfs(l,i-1)root.Right=dfs(i+1,r)returnroot}returndfs(0,len(nums)-1)}
/** * Definition for a binary tree node. * type TreeNode struct { * Val int * Left *TreeNode * Right *TreeNode * } */funcconstructMaximumBinaryTree(nums[]int)*TreeNode{d:=make([]int,1010)fori,v:=rangenums{d[v]=i+1}tree:=newSegmentTree(nums)vardfsfunc(l,rint)*TreeNodedfs=func(l,rint)*TreeNode{ifl>r{returnnil}val:=tree.query(1,l,r)root:=&TreeNode{Val:val}root.Left=dfs(l,d[val]-1)root.Right=dfs(d[val]+1,r)returnroot}returndfs(1,len(nums))}typenodestruct{lintrintvint}typesegmentTreestruct{nums[]inttr[]*node}funcnewSegmentTree(nums[]int)*segmentTree{n:=len(nums)tr:=make([]*node,n<<2)fori:=rangetr{tr[i]=&node{}}t:=&segmentTree{nums,tr}t.build(1,1,n)returnt}func(t*segmentTree)build(u,l,rint){t.tr[u].l,t.tr[u].r=l,rifl==r{t.tr[u].v=t.nums[l-1]return}mid:=(l+r)>>1t.build(u<<1,l,mid)t.build(u<<1|1,mid+1,r)t.pushup(u)}func(t*segmentTree)query(u,l,rint)int{ift.tr[u].l>=l&&t.tr[u].r<=r{returnt.tr[u].v}mid:=(t.tr[u].l+t.tr[u].r)>>1v:=0ifl<=mid{v=t.query(u<<1,l,r)}ifr>mid{v=max(v,t.query(u<<1|1,l,r))}returnv}func(t*segmentTree)pushup(uint){t.tr[u].v=max(t.tr[u<<1].v,t.tr[u<<1|1].v)}
Solution 3
Thinking
The segment tree is extra structure. A decreasing stack finds the nearest greater neighbor on the left: the last popped node becomes the left child, and the new top takes the current node as its right child. One pass builds the tree.
1 2 3 4 5 6 7 8 910111213141516171819
# Definition for a binary tree node.# class TreeNode:# def __init__(self, val=0, left=None, right=None):# self.val = val# self.left = left# self.right = rightclassSolution:defconstructMaximumBinaryTree(self,nums:list[int])->Optional[TreeNode]:stk=[]forvinnums:node=TreeNode(v)last=Nonewhilestkandstk[-1].val<v:last=stk.pop()node.left=lastifstk:stk[-1].right=nodestk.append(node)returnstk[0]
/** * Definition for a binary tree node. * type TreeNode struct { * Val int * Left *TreeNode * Right *TreeNode * } */funcconstructMaximumBinaryTree(nums[]int)*TreeNode{stk:=[]*TreeNode{}for_,v:=rangenums{node:=&TreeNode{Val:v}varlast*TreeNodeforlen(stk)>0&&stk[len(stk)-1].Val<v{last=stk[len(stk)-1]stk=stk[:len(stk)-1]}node.Left=lastiflen(stk)>0{stk[len(stk)-1].Right=node}stk=append(stk,node)}returnstk[0]}
Solution 4: Explicit Stack
Thinking
The root is the maximum of an interval, and both children follow the same rule. \(n \le 1000\), so a linear scan for each maximum is acceptable in time. On an increasing array the maximum is always at the right end, so the first call always handles the remaining \(n-1\) elements on the left and the depth is \(n\), past the default recursion limit.
The root of a segment depends only on its two endpoints, so the subarray does not need to be copied.
Store pending half-open intervals \([l, r)\) on an explicit stack, together with which side of the parent they hang from. Pop an interval, scan for its maximum, create the node, then push the right interval and the left interval. An empty interval is skipped.
Scan the half-open interval \([l, r)\) for the index \(i\) of the maximum, and make \(\textit{nums}[i]\) the root. The left child is the interval \([l, i)\) and the right child is \([i + 1, r)\). Pending intervals live on an explicit stack: after a root is created, push the right interval and then the left interval. Empty intervals are skipped. The first interval is \([0, n)\).
The time complexity is \(O(n^2)\), and the space complexity is \(O(n)\), where \(n\) is the length of the array.
/** * Definition for a binary tree node. * type TreeNode struct { * Val int * Left *TreeNode * Right *TreeNode * } */funcconstructMaximumBinaryTree(nums[]int)*TreeNode{typeframestruct{l,r,sideintparent*TreeNode}n:=len(nums)varroot*TreeNodestk:=[]frame{{0,n-1,0,nil}}forlen(stk)>0{cur:=stk[len(stk)-1]stk=stk[:len(stk)-1]ifcur.l>cur.r{continue}i:=cur.lforj:=cur.l;j<=cur.r;j++{ifnums[j]>nums[i]{i=j}}node:=&TreeNode{Val:nums[i]}ifcur.parent==nil{root=node}elseifcur.side==0{cur.parent.Left=node}else{cur.parent.Right=node}stk=append(stk,frame{i+1,cur.r,1,node},frame{cur.l,i-1,0,node})}returnroot}
/** * Definition for a binary tree node. * struct TreeNode { * int val; * struct TreeNode *left; * struct TreeNode *right; * }; */structTreeNode*constructMaximumBinaryTree(int*nums,intnumsSize){typedefstruct{intl,r,side;structTreeNode*parent;}Frame;Frame*stk=(Frame*)malloc(sizeof(Frame)*(numsSize*2+5));inttop=0;stk[top++]=(Frame){0,numsSize,0,NULL};structTreeNode*root=NULL;while(top){Framecur=stk[--top];if(cur.l>=cur.r){continue;}intidx=cur.l;for(inti=cur.l+1;i<cur.r;++i){if(nums[i]>nums[idx]){idx=i;}}structTreeNode*node=(structTreeNode*)malloc(sizeof(structTreeNode));node->val=nums[idx];node->left=node->right=NULL;if(cur.parent==NULL){root=node;}elseif(cur.side==0){cur.parent->left=node;}else{cur.parent->right=node;}stk[top++]=(Frame){idx+1,cur.r,1,node};stk[top++]=(Frame){cur.l,idx,0,node};}free(stk);returnroot;}
Solution 5: Segment Tree + Explicit Stack
Thinking
Solution 1 builds the tree by recursion, but each maximum is still a linear scan. A segment tree answers a range maximum in \(O(\log n)\). The left interval can still form a chain of length \(n\), so those two child intervals go on the same explicit stack. The total time is \(O(n \log n)\).
/** * Definition for a binary tree node. * type TreeNode struct { * Val int * Left *TreeNode * Right *TreeNode * } */funcconstructMaximumBinaryTree(nums[]int)*TreeNode{d:=make([]int,1010)fori,v:=rangenums{d[v]=i+1}tree:=newSegmentTree(nums)typeframestruct{l,r,sideintparent*TreeNode}varroot*TreeNodestk:=[]frame{{1,len(nums),0,nil}}forlen(stk)>0{cur:=stk[len(stk)-1]stk=stk[:len(stk)-1]ifcur.l>cur.r{continue}val:=tree.query(1,cur.l,cur.r)node:=&TreeNode{Val:val}ifcur.parent==nil{root=node}elseifcur.side==0{cur.parent.Left=node}else{cur.parent.Right=node}i:=d[val]stk=append(stk,frame{i+1,cur.r,1,node},frame{cur.l,i-1,0,node})}returnroot}typenodestruct{lintrintvint}typesegmentTreestruct{nums[]inttr[]*node}funcnewSegmentTree(nums[]int)*segmentTree{n:=len(nums)tr:=make([]*node,n<<2)fori:=rangetr{tr[i]=&node{}}t:=&segmentTree{nums,tr}t.build(1,1,n)returnt}func(t*segmentTree)build(u,l,rint){t.tr[u].l,t.tr[u].r=l,rifl==r{t.tr[u].v=t.nums[l-1]return}mid:=(l+r)>>1t.build(u<<1,l,mid)t.build(u<<1|1,mid+1,r)t.pushup(u)}func(t*segmentTree)query(u,l,rint)int{ift.tr[u].l>=l&&t.tr[u].r<=r{returnt.tr[u].v}mid:=(t.tr[u].l+t.tr[u].r)>>1v:=0ifl<=mid{v=t.query(u<<1,l,r)}ifr>mid{v=max(v,t.query(u<<1|1,l,r))}returnv}func(t*segmentTree)pushup(uint){t.tr[u].v=max(t.tr[u<<1].v,t.tr[u<<1|1].v)}