O(h)时间复杂度拆分二叉搜索树问题调试求助
Hey there, let's work through your tree split problem. I've examined your code and the specific test case you mentioned, and I can spot two critical issues causing the incorrect split and missing nodes.
Key Bugs in Your Current Code
Lost Nodes Due to Premature Link Breaking
In the first branch of your loop (when you take the current node and its left subtree), you immediately setprev->rc = NULLafter moving tocurrent->rc. This disconnects the right subtree of the current node (like the 30 in your test case) from both trees, leaving it orphaned. That's why your split result only has 170 and 45—30 gets lost entirely.Inefficient and Incorrect lcnt Updates
- Using
count_total_tasksto recalculate lcnt values in the else branch introduces O(n) time complexity, which violates your requirement of O(h) performance. - The
updtfunction blindly decrements the lcnt of all nodes along the left path ofnew_root_2, which isn't correct. Only specific ancestor nodes (those whose left subtree had nodes split off) need their lcnt adjusted, not every left-path node.
- Using
Fix: Recursive Split Implementation (O(h) Time)
A recursive approach is cleaner for this problem, as it naturally traverses the tree in O(h) time and makes lcnt maintenance straightforward. Here's a corrected implementation that adheres to your requirements:
// Helper to calculate total nodes in a subtree (for validation, not required in core logic) int get_total_nodes(struct tree *node) { if (!node) return 0; return node->lcnt + 1 + get_total_nodes(node->rc); } struct tree* split(struct tree *root, struct tree **new_tree, int collect) { if (!root) { *new_tree = NULL; return NULL; } int left_subtree_size = root->lcnt; // Total nodes in root's left subtree if (collect <= left_subtree_size) { // Split the left subtree: take 'collect' nodes from it struct tree *remaining_left; struct tree *tree1 = split(root->lc, &remaining_left, collect); // Update root's lcnt (subtract the number of nodes we took from the left) root->lcnt = left_subtree_size - collect; root->lc = remaining_left; *new_tree = root; return tree1; } else if (collect == left_subtree_size + 1) { // Split exactly at the root: tree1 is root + left subtree, tree2 is right subtree *new_tree = root->rc; root->rc = NULL; // Disconnect root from tree2 return root; } else { // Split the right subtree: take (collect - left_subtree_size - 1) nodes from it struct tree *taken_right; struct tree *tree2 = split(root->rc, &taken_right, collect - (left_subtree_size + 1)); // Attach the taken part of the right subtree to root root->rc = taken_right; *new_tree = tree2; return root; } }
How This Fixes Your Test Case
Let's walk through your test tree (assuming lcnt is correctly set to represent the total number of nodes in the left subtree):
- Tree structure:
170 (lcnt=2, lc=45, rc=NULL)→45 (lcnt=0, lc=NULL, rc=30)→30 (lcnt=0, lc=NULL, rc=NULL) - If you want to split into the first 2 nodes (45 and 30) and the remaining 1 node (170):
- Call
split(170, &tree2, 2):left_subtree_size=2, so we split the left subtree (45) for collect=2. - Call
split(45, &remaining_left, 2):left_subtree_size=0, collect>0+1=1, so split the right subtree (30) for collect=1. - Call
split(30, &taken_right, 1):left_subtree_size=0, collect=0+1, so return 30 as tree1,taken_right=NULL. - Back to
split(45, ...): Attachtaken_right=NULLto 45's rc, return 45 as tree1,remaining_left=NULL. - Back to
split(170, ...): Update 170's lcnt to 2-2=0, set its lc toremaining_left=NULL, return 45 (with rc=30) as tree1,tree2=170.
- Call
This gives you the expected split: tree1 has 45 and 30, tree2 has 170.
Notes for Your Iterative Code
If you prefer to stick with an iterative approach, you'll need to:
- Avoid breaking links prematurely—only disconnect nodes once you've determined which tree they belong to.
- Track ancestor nodes that need their lcnt adjusted, rather than traversing entire subtrees.
- Ensure all nodes are attached to either tree1 or tree2 before exiting the loop.
内容的提问来源于stack exchange,提问作者Alexis Tevre

