抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

二叉搜索树是有序的。

它可以完成搜索插入以及删除等操作。

搜索

例题

思考

因为二叉搜索树有以下几个性质。

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉搜索树。

所以它的搜索效率就跟二分搜索一样很快。

使用迭代法很快就能写出来。

代码

1
2
3
4
5
6
7
8
9
10
TreeNode* searchNode(TreeNode* root, int target)
{
while (root)
{
if (target == root->val) return root; //找到对应值
if (target < root->val) root = root->left; //目标值比当前节点小,说明目标值可能在左子树
if (target > root->val) root = root->right; //目标值比当前节点大,说明目标值可能在右子树
}
return nullptr;
}

插入

例题

思考

插入就是在搜索的基础上,如果找不到这个节点,那么找个空的位置插入进去。

但在实现上,我们需要记录当前节点以及它的父节点。

否则会发现当前节点是空的之后,丢失了父节点的指针,导致无法建立起联系。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
TreeNode* insertNode(TreeNode* root, int num)
{
if (!root)
{
TreeNode* node = new TreeNode(num);
return node;
}
TreeNode* cur = root;
TreeNode* parent = root;
//寻找插入位置
while (cur)
{
parent = cur; //当cur是空节点时会直接跳出循环,parent不会更新,为cur的父节点。
if (num < cur->val) cur = cur->left;
else cur = cur->right;
}
//插入节点
TreeNode* node = new TreeNode(num);
if (num < parent->val) parent->left = node;
if (num > parent->val) parent->right = node;
return root;
}

删除

例题

思考

首先要找到删除的节点,这个用前面的searchNode()函数就可以

因为要将删除节点的子树连接回去,所以我们要找到的是匹配节点的父节点(就像插入函数那样),直接用搜索函数不行。

麻烦在于删除后结构的调整。

删除一个节点有几种情况呢?

  1. 没找到删除节点,直接返回原来的树
  2. 找到删除节点,没有子节点,直接把他变成NULL删除
  3. 找到删除节点,有左子节点或右子节点,删去自己,把左子节点或右子节点变成根节点。
  4. 找到删除节点,同时拥有左右子节点,删去自己,左子树移到右子树的最左边左孩子上,右子节点成为根节点。

第四种情况有些难理解。

因为要保证树的有序,左子树上所有节点肯定是小于右子树任一一个节点的。

把它整个移到右子树的左叶子节点上就不会破坏有序。

代码

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
TreeNode* deleteNode(TreeNode* root, int target)
{
TreeNode* node = root;
TreeNode* parent = nullptr;
//寻找插入位置
while (node)
{
if (node->val == target) break;
parent = node; //当cur是空节点时会直接跳出循环,parent不会更新,为cur的父节点。
if (target < node->val) node = node->left;
else node = node->right;
}
if (!node) return root; //情况一,没找到删除节点
if (!node->left && !node->right) //情况二,没有左子节点和右子节点
{
if (parent->left->val == target) parent->left = nullptr;
if (parent->right->val == target) parent->right = nullptr;
return root;
}
if (node->left && node->right) //情况四,左子结点和右子节点都有
{
TreeNode* cur = node->right;
while (cur->left) cur = cur->left; //前往右子树的最左边
cur->left = node->left; //左子树连接上右子树的最左边
if (parent->left && parent->left->val == target) //删除节点是父节点的左子结点
parent->left = node->right;
if (parent->right && parent->right->val == target) //删除节点是父节点的右子结点
parent->right = node->right;
}
//情况三,仅有一个子节点
if (parent->left->val == target) //删除节点是父节点的左子结点
parent->left = node->left ? node->left : node->right; //删除节点有的是左子结点还是右子节点
if (parent->right->val == target) //删除节点是父节点的右子结点
parent->right = node->left ? node->left : node->right; //删除节点有的是左子结点还是右子节点
return root;
}

小结

至此二叉树的一些基础操作就结束了。其他变形题都可以根据这几种基础操作的思想。

可以发现需要改变二叉树的结构往往都会复杂许多。

如构造,删除等操作。

不过在写这几篇博文的过程中也确实学到许多东西啦。对递归和指针的理解加深了许多。

评论