二叉搜索树的验证、查找、插入和删除
拔丝地瓜

二叉搜索树的查找、插入和删除

二叉搜索树

二叉搜索树又被称为二叉排序树,它本身也是一棵二叉树,且满足以下性质:

  1. 若左子树不为空,则左子树上左右节点的值都小于根节点的值
  2. 若它的右子树不为空,则它的右子树上所有的节点的值都大于根节点的值
  3. 它的左右子树也要分别是二叉搜索树
1
2
3
4
5
6
7
8
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) {}
};

验证是否为二叉搜索树

递归法

利用有效二叉树的定义:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool helper(TreeNode* root, long long lower, long long upper){
if (root == nullptr){
return true;
}
if (root->val <= lower || root->val >= upper){
return false;
}
return helper(root->left, lower, root->val)
&& helper(root->right, root->val, upper);
}

bool isValidBST(TreeNode* root) {
return helper(root, LONG_MIN, LONG_MAX);
}

中序遍历

中序遍历结果应为递增序列

二叉搜索树的查找

1
2
3
4
5
6
7
8
9
TreeNode* searchBST(TreeNode* root, int val) {
if (root == nullptr){
return nullptr;
}
if (root->val == val){
return root;
}
return root->val > val ? searchBST(root->left, val) : searchBST(root->right, val);
}

二叉搜素树的插入

当将 val插入到以 root为根的子树上时,根据 valroot.val的大小关系,就可以确定要将 val插入到哪个子树中:

  • 如果该子树不为空,则问题转化成了将 val插入到对应子树上
  • 否则,在此处新建一个以 val为值的节点,并链接到其父节点 root
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (root == nullptr){
return new TreeNode(val);
}
TreeNode* pos = root;
while (pos != nullptr){
if (pos->val < val){
if (pos->right == nullptr){
pos->right = new TreeNode(val);
break;
} else{
pos = pos->right;
}
} else{
if (pos->left == nullptr){
pos->left = new TreeNode(val);
break;
} else{
pos = pos->left;
}
}
}
return root;
}

二叉搜索树的的删除

删除操作比较复杂,可以分为以下三种情况:

  • 被删除节点为叶子节点:直接删除该节点
  • 被删除结点有一个左孩子或一个右孩子:将孩子结点设为该结点的父结点的孩子后,即可删除该结点
  • 被删除结点有两个孩子结点:一般的删除策略是,用被删除结点的右子树中的最小结点替代被删除结点,并递归地删除这个最小数据结点

利用前继和后继节点也可以分为以下三种情况:

  • 要删除的节点为叶子节点,可以直接删除

  • 要删除的节点不是叶子节点且拥有右节点,则该节点可以由该节点的后继节点进行替代,该后继节点位于右子树中较低的位置。然后可以从后继节点的位置递归向下操作以删除后继节点

  • 删除的节点不是叶子节点且拥有右节点,则该节点可以由该节点的后继节点进行替代,该后继节点位于右子树中较低的位置。然后可以从后继节点的位置递归向下操作以删除后继节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//寻找后继节点
TreeNode* successor(TreeNode* root){
root = root->right;
while (root->left != nullptr){
root = root->left;
}
return root;
}

//寻找前继节点
TreeNode* predecessor(TreeNode* root){
root = root->left;
while (root->right != nullptr){
root = root->right;
}
return root;
}

TreeNode* deleteNode(TreeNode* root, int key) {
if (root == nullptr){
return nullptr;
}
if (root->val > key){ //在左子树中寻找要删除的节点
root->left = deleteNode(root->left, key);
} else if(root->val < key){ //在右子树中寻找要删除的节点
root->right = deleteNode(root->right, key);
} else{ //找到要删除的节点
if (root->left == nullptr && root->right == nullptr){
//如果是叶子节点,则直接删除
root = nullptr;
} else if (root->right != nullptr){
//如果有右子树,则用其后继值代替,并递归的删除这个后继节点
root->val = successor(root)->val;
root->right = deleteNode(root->right, root->val);
} else{
//如果左右子树,则用其后继值代替,并递归的删除这个后继节点
root->val = predecessor(root)->val;
root->left = deleteNode(root->left, root->val);
}
}
return root;
}

//注意:这里的前继节点和后继节点并不是严格意义上的,而是建立在左(右)子树存在的基础上的