
Problem Statement
The task involves splitting a binary search tree (BST) into two distinct subtrees based on a given target value. The first subtree should include all nodes whose values are less than or equal to the target, and the second subtree should contain nodes with values greater than the target. It's important to note that the target value does not necessarily exist within the original BST. The challenge lies in ensuring that the structure of the original BST is preserved as much as possible during the split. This means that for any child node 'c' and its parent 'p' in the original tree, if both are in the same subtree after the division, 'c' should maintain 'p' as its parent. The solution should return the roots of these two resulted subtrees.
Examples
Example 1
Input:
root = [4,2,6,1,3,5,7], target = 2
Output:
[[2,1],[4,3,6,null,null,5,7]]
Example 2
Input:
root = [1], target = 1
Output:
[[1],[]]
Constraints
- The number of nodes in the tree is in the range
[1, 50]
. 0 <= Node.val, target <= 1000
Approach and Intuition
To solve this problem, let's break down the operations we need to perform based on the given examples and constraints:
Recursive Traversal: We'll employ a depth-first search (DFS) to traverse the BST. This technique ensures we examine each node, deciding whether it belongs in the left or right subtree relative to the target.
Split Logic:
- If the current node's value is less than or equal to the target, it goes into the left subtree.
- If the current node's value is more than the target, it falls into the right subtree.
Maintaining Structure:
- While moving nodes to the respective subtree, care must be taken to maintain the parent-child relationship to preserve the structure of the original tree.
Edge Cases and Constraints:
- If the tree comprises only one node or all nodes are either less than or more than the target, appropriate null trees or node placements should be handled.
- With node values and targets ranging only between 0 to 1000 and tree size between 1 and 50, the operations must be efficient to handle these upper limits effectively.
By maintaining a clear divide based on the node values relative to the target, and carefully re-linking parents to children, this approach ensures that the subtrees reflect the correct structure and value order as stipulated by the binary search tree properties.
Solutions
- C++
class Solution {
public:
vector<TreeNode*> partitionBST(TreeNode* root, int value) {
TreeNode* leftDummy = new TreeNode(0);
TreeNode* left = leftDummy;
TreeNode* rightDummy = new TreeNode(0);
TreeNode* right = rightDummy;
TreeNode* node = root;
TreeNode* temp = nullptr;
while (node != nullptr) {
if (node->val <= value) {
left->right = node;
left = node;
temp = node->right;
left->right = nullptr;
node = temp;
} else {
right->left = node;
right = node;
temp = node->left;
right->left = nullptr;
node = temp;
}
}
return {leftDummy->right, rightDummy->left};
}
};
The provided C++ function partitionBST
splits a binary search tree into two subtrees based on a specified value. The function returns a vector
containing pointers to the roots of the two resulting subtrees: one subtree contains all nodes with values less than or equal to the given value, and the other contains nodes with values greater.
- Initialize two dummy nodes,
leftDummy
andrightDummy
, to simplify the process of building the left and right subtrees. - Use
left
andright
pointers to manage the current end of the growing subtrees. - Traverse the given BST using a
node
pointer. Compare the value of the currentnode
with the input value:- If the node's value is less than or equal to the provided value, attach it to the right child of the
left
node. Move theleft
pointer to this node and sever the node's right link to isolate it from its original tree structure before moving on. - If the node's value is greater than the input value, attach it to the left child of the
right
node. Move theright
pointer and sever the node's left link.
- If the node's value is less than or equal to the provided value, attach it to the right child of the
- Continue this process until no nodes are left to process.
- The function concludes by returning a vector of two elements: the right child of
leftDummy
and the left child ofrightDummy
. These represent the roots of the two required subtrees.
With this approach, ensure all nodes get correctly reassigned to the appropriate subtree without losing any nodes or reference links.
- Java
class Solution {
public static TreeNode[] divideBST(TreeNode rootNode, int splitVal) {
// Create placeholders for smaller and larger partitions
TreeNode smallDummy = new TreeNode(0);
TreeNode smallCurrent = smallDummy;
TreeNode largeDummy = new TreeNode(0);
TreeNode largeCurrent = largeDummy;
// Begin with the root node
TreeNode iterate = rootNode;
TreeNode futureNode;
while (iterate != null) {
if (iterate.val <= splitVal) {
// Link iterate node to small partition
smallCurrent.right = iterate;
smallCurrent = iterate;
// Go to right child since left is guaranteed smaller
futureNode = iterate.right;
// Detach current node from its right child
smallCurrent.right = null;
iterate = futureNode;
} else {
// Link iterate node to large partition
largeCurrent.left = iterate;
largeCurrent = iterate;
// Go to left child since right is guaranteed bigger
futureNode = iterate.left;
// Detach current node from its left child
largeCurrent.left = null;
iterate = futureNode;
}
}
// Return trees from dummy nodes
return new TreeNode[] { smallDummy.right, largeDummy.left };
}
}
The Split BST problem involves dividing a Binary Search Tree (BST) into two separate trees based on a given value: splitVal
. The trees returned are such that one contains all values less than or equal to splitVal
, and the other contains all values greater than splitVal
. Here's how the solution in Java addresses this problem:
Initialize two dummy nodes,
smallDummy
andlargeDummy
, to act as placeholders for the roots of the two new trees. Additional pointers,smallCurrent
andlargeCurrent
, are used to build these trees iteratively.Start from the root node of the given BST and traverse it. Based on the comparison of the current node's value with
splitVal
, decide in which tree the current node should be placed:- If the current node value is less than or equal to
splitVal
, attach it to the right of thesmallCurrent
tree. Then movesmallCurrent
to point to this node. - If the current node value is greater than
splitVal
, attach it to the left of thelargeCurrent
tree. Then movelargeCurrent
to point to this node.
- If the current node value is less than or equal to
After linking a node to its respective tree, detach it from its current subtree to avoid lingering connections that might cause a cycle or incorrect tree structure.
Continue the traversal by moving to the next relevant child node that could potentially contain values satisfying the conditions for the respective sub-trees (right child for the small tree, left child for the large tree).
Once the entire BST has been traversed and all nodes have been appropriately placed, the trees are extracted from the dummy nodes. The right child of
smallDummy
becomes the root of the small tree, and the left child oflargeDummy
becomes the root of the large tree.Finally, return these roots as an array of nodes, completing the division of the BST based on
splitVal
.
This method ensures that the BST is split based on the value, maintaining the properties of BSTs in both resultant trees.
- Python
class Solution:
def divideBST(self, root: TreeNode, limit: int) -> list[TreeNode]:
# Temporary nodes for constructing smaller and larger BST parts
small_root_placeholder = TreeNode(0)
small_current = small_root_placeholder
large_root_placeholder = TreeNode(0)
large_current = large_root_placeholder
# Main loop to navigate tree
node_pointer = root
next_pointer = None
while node_pointer is not None:
if node_pointer.val <= limit:
# Link node to the 'less than or equal' part
small_current.right = node_pointer
small_current = node_pointer
# Prepare for the next iteration on the right subtree
next_pointer = node_pointer.right
small_current.right = None
node_pointer = next_pointer
else:
# Link node to the 'greater than' part
large_current.left = node_pointer
large_current = node_pointer
# Prepare for the next iteration on the left subtree
next_pointer = node_pointer.left
large_current.left = None
node_pointer = next_pointer
# Return both parts of the divided BST
return [small_root_placeholder.right, large_root_placeholder.left]
The provided Python solution defines a function divideBST
within a Solution
class to split a binary search tree (BST) based on a defined limit. The function splits the BST into two sub-trees: one containing nodes with values less than or equal to the limit, and the other containing nodes with values greater than the limit.
Here's how the function operates:
- Initializes placeholders for the roots of two new BSTs,
small_root_placeholder
for nodes less than or equal tolimit
, andlarge_root_placeholder
for nodes greater thanlimit
. - Iteratively traverses through the original BST using
node_pointer
. During each iteration, it checks the current node's value againstlimit
:- If the current node's value is less than or equal to
limit
, it gets appended to the right of thesmall_current
tree. - If the current node's value exceeds
limit
, it gets appended to the left of thelarge_current
tree.
- If the current node's value is less than or equal to
- After each iteration, pointers are adjusted appropriately to exclude the linked node from further consideration, preventing duplication in the split BSTs.
- The traversal continues until all nodes have been considered and properly allocated to either the
small_root_placeholder
orlarge_root_placeholder
sub-tree. - The function returns both parts of the divided BST as a list containing the
right
child ofsmall_root_placeholder
and theleft
child oflarge_root_placeholder
, which represent the two new root nodes, respectively.
The algorithm efficiently divides the entire BST with a single tree traversal, ensuring minimal time complexity ideally close to O(n), where n is the number of nodes in the BST. It uses direct pointer manipulation to build the new trees, avoiding the creation of additional data structures, thus optimizing memory usage.
No comments yet.