[Google] Inorder Successor in Binary Search Tree, Solution

本文介绍如何在二叉搜索树中找到给定节点的中序后继节点,提供了两种方法:一种利用父指针,另一种不使用父指针。详细解释了每种方法的具体步骤,并给出了相应的时间复杂度。

In Binary Tree, Inorder successor of a node is the next node in Inorder traversal of the Binary Tree. Inorder Successor is NULL for the last node in Inoorder traversal.
In Binary Search Tree, Inorder Successor of an input node can also be defined as the node with the smallest key greater than the key of input node. So, it is sometimes important to find next node in sorted order.

In the above diagram, inorder successor of 8 is 10, inorder successor of 10 is 12 and inorder successor of 14 is 20.
Method 1 (Uses Parent Pointer)
In this method, we assume that every node has parent pointer.
The Algorithm is divided into two cases on the basis of right subtree of the input node being empty or not.
Input: node, root // node is the node whose Inorder successor is needed.
output: succ // succ is Inorder successor of node.
1) If right subtree of node is not NULL, then succ lies in right subtree. Do following.
Go to right subtree and return the node with minimum key value in right subtree.
2) If right sbtree of node is NULL, then succ is one of the ancestors. Do following.
Travel up using the parent pointer until you see a node which is left child of it’s parent. The parent of such a node is the succ.
Implementation
Note that the function to find InOrder Successor is highlighted (with gray background) in below code.

1:  #include <stdio.h>  
2: #include <stdlib.h>
3: /* A binary tree node has data, pointer to left child
4: and a pointer to right child */
5: struct node
6: {
7: int data;
8: struct node* left;
9: struct node* right;
10: struct node* parent;
11: };
12: struct node * minValue(struct node* node);
13: struct node * inOrderSuccessor(struct node *root, struct node *n)
14: {
15: // step 1 of the above algorithm
16: if( n->right != NULL )
17: return minValue(n->right);
18: // step 2 of the above algorithm
19: struct node *p = n->parent;
20: while(p != NULL && n == p->right)
21: {
22: n = p;
23: p = p->parent;
24: }
25: return p;
26: }
27: /* Given a non-empty binary search tree, return the minimum data
28: value found in that tree. Note that the entire tree does not need
29: to be searched. */
30: struct node * minValue(struct node* node) {
31: struct node* current = node;
32: /* loop down to find the leftmost leaf */
33: while (current->left != NULL) {
34: current = current->left;
35: }
36: return current;
37: }

Time Complexity: O(h) where h is height of tree.

Method 2 (Don’t Use Parent Pointer)
Inorder travel the tree and
1) If current visit node is target node,  mark the indicator as true.
2) If indicator is true, print the node and return.

Implementation

1:  struct node * inOrderSuccessor(struct node *n, struct node* target, bool& indicator)  
2: {
3: if( n== NULL )
4: return NULL;
5: if(indicator) return n;
6: if(n == target) { indicator = true; return;}
7: node* left = inOrderSuccessor(n->left, target, indicator);
8: node * right =inOrderSuccessor(n->right, target, indicator);
9: if(left != NULL) return left;
10: if(right!= NULL) return right;
11: return NULL;
12: }
<think>我们正在处理一个二叉搜索树(BST)中删除节点的C++函数问题,并分析其时间复杂度。 根据引用[1],二叉搜索树支持插入、删除、搜索等操作,平均时间复杂度为O(log n)。 删除二叉搜索树中的节点需要考虑三种情况: 1. 节点是叶子节点(没有子节点):直接删除。 2. 节点有一个子节点:用子节点替换该节点。 3. 节点有两个子节点:找到该节点的后继节点(右子树中的最小节点)或前驱节点(左子树中的最大节点),用后继节点的值替换该节点的值,然后删除后继节点(后继节点没有左子树,所以转换为情况1或2)。 步骤: 1. 首先,我们需要找到要删除的节点。 2. 然后根据上述三种情况进行处理。 我们将定义一个TreeNode结构,假设树节点包含key和可能的左右子节点指针。 时间复杂度分析: 平均情况下,二叉搜索树的高度为O(log n),因此删除操作需要先找到节点(O(log n)),然后在删除时可能需要找到后继节点(这也需要O(log n)),所以总的时间复杂度为O(log n)。 最坏情况下(树退化为链表),时间复杂度为O(n)。 注意:我们考虑的是平均情况,所以引用[1]中提到的平均时间复杂度O(log n)是适用的。 现在,我们编写C++函数。 假设树节点的定义如下: struct TreeNode { int key; TreeNode *left; TreeNode *right; TreeNode(int x) : key(x), left(nullptr), right(nullptr) {} }; 函数返回删除后的树的根节点(因为根节点可能被删除)。 我们按照以下步骤: 1. 如果树为空,返回空。 2. 如果要删除的键小于当前节点的键,递归到左子树。 3. 如果要删除的键大于当前节点的键,递归到右子树。 4. 如果等于,则进行删除: a. 如果没有左子节点,则用右子节点替代(包括右子节点为空的情况)。 b. 如果没有右子节点,则用左子节点替代。 c. 如果都有,则找到右子树中的最小节点(最左边的节点),将当前节点的键替换为该最小节点的键,然后递归删除右子树中的那个最小节点。 注意:也可以使用前驱(左子树中的最大节点)代替,这里我们使用后继(右子树的最小节点)。 实现: 此外,我们还需要一个辅助函数来找到右子树的最小节点(或者直接递归删除时也可以一起完成)。 但是注意:在找到后继节点的键后,我们删除后继节点时,由于后继节点是最左节点,所以它一定没有左子节点(因此删除它只需要用它的右子节点替代即可,即情况a或b)。 因此,我们可以在删除两个子节点的情况下,先找到右子树的最小节点的键,然后赋值给当前节点,然后递归地从右子树中删除那个最小节点。 另一种做法是写一个单独的函数来获取最小节点并删除,但为了清晰,我们可以在递归删除中完成。 代码实现如下: </think>### 二叉搜索树删除节点的 C++ 实现及时间复杂度分析 以下是删除二叉搜索树(BST)中指定键值对的 C++ 实现,包含详细注释: ```cpp #include <iostream> struct TreeNode { int key; TreeNode* left; TreeNode* right; TreeNode(int k) : key(k), left(nullptr), right(nullptr) {} }; // 辅助函数:找到子树的最小节点 TreeNode* findMin(TreeNode* node) { while (node->left != nullptr) { node = node->left; } return node; } // 删除指定键的节点 TreeNode* deleteNode(TreeNode* root, int k) { if (root == nullptr) return root; // 空树直接返回 // 递归查找目标节点 if (k < root->key) { root->left = deleteNode(root->left, k); } else if (k > root->key) { root->right = deleteNode(root->right, k); } else { // 找到目标节点(三种情况处理) // 情况1: 无子节点或只有一个子节点 if (root->left == nullptr) { TreeNode* temp = root->right; delete root; return temp; } else if (root->right == nullptr) { TreeNode* temp = root->left; delete root; return temp; } // 情况2: 有两个子节点 TreeNode* successor = findMin(root->right); // 找到后继节点 root->key = successor->key; // 复制后继节点的值 root->right = deleteNode(root->right, successor->key); // 删除后继节点 } return root; } ``` ### 时间复杂度分析 1. **最优/平均情况**:当 BST 保持平衡时: - 查找目标节点:$O(\log n)$ - 寻找后继节点:$O(\log n)$ - **总复杂度**:$O(\log n)$ (符合引用[1]中 BST 操作的平均时间复杂度[^1]) 2. **最坏情况**(树退化为链表): - 查找目标节点:$O(n)$ - 寻找后继节点:$O(n)$ - **总复杂度**:$O(n)$ ### 操作说明 1. 当节点无子节点时,直接删除(叶子节点) 2. 当节点有一个子节点时,用子节点替代 3. 当节点有两个子节点时: - 找到右子树的最小节点(后继节点) - 用后继节点的值覆盖目标节点 - 递归删除后继节点 (路径定义符合引用[3]的节点序列描述[^3]) ### 验证示例 ```cpp // 中序遍历验证函数 void inorder(TreeNode* root) { if (root) { inorder(root->left); std::cout << root->key << " "; inorder(root->right); } } int main() { /* 构建示例树: 50 / \ 30 70 / \ / \ 20 40 60 80 */ TreeNode* root = new TreeNode(50); root->left = new TreeNode(30); root->right = new TreeNode(70); root->left->left = new TreeNode(20); root->left->right = new TreeNode(40); root->right->left = new TreeNode(60); root->right->right = new TreeNode(80); std::cout << "原始树: "; inorder(root); // 输出: 20 30 40 50 60 70 80 root = deleteNode(root, 50); // 删除根节点 std::cout << "\n删除后: "; inorder(root); // 输出: 20 30 40 60 70 80 } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值