数据结构之树(2)——二叉查找树和二叉排序树

二叉查找树

二叉树包含许多特殊的形式,每一种形式都有自己的作用,但是其最主要的应用还在于进行查找操作和维持相对顺序这两个方面。

二叉查找树的定义(Binary Search Tree):

  • 该树是一颗二叉树。
  • 如果左子树不为空,则左子树上所有节点的值均小于根节点的值。
  • 如果右子树不为空,则右子树上所有节点的值均大于根节点的值。
  • 左、右子树也都是二叉查找树。

二叉查找树就是为了快速查找某个元素。

例如要查找值为1的结点,步骤如下:

第一步:毕竟1与根节点的值,结果1小于6,向右查找,找到6的右孩子值为3的结点。

第二步:比较1与3,发现1还是小于3,那么找到值为3的结点的右孩子2。

第三步:比较1与2,发现1还是小于2,那么查找到值为2的结点的右孩子。

第四步:发现1等于1,那么该结点就是我们所要查找的结点。

可以发现二叉查找树的时间复杂度为O(logn),和树的深度一样。

而且类似于二分查找(二分查找要求数组元素是有序的)。

二叉排序树

二叉查找树又被称为二叉排序树,因为二叉查找树的定义要求了左子树小于父结点,右子树大于父结点,保证了二叉树是有序的。

而新插入的结点同样要遵循这一要求,所以也是有序的。

例如,新插入一个结点值为5的元素,按照二叉排序树的规则,由于5<6,5>3,5>4,所以最终会插入到结点4的右孩子位置。

但如果有这么一种情况,插入的结点大部分是右孩子或者左孩子,那么该二叉排序树的查找时间就不再是O(logn)了,而是O(n)。

例如要查找结点值为9的结点,那么就需要遍历比较3、4、6、7、8结点的值,查询的时间复杂度大大提升了。

这种二叉树已经失去了平衡。

解决已经失去平衡的二叉树的方法有AVL树、红黑树等。

除此之外,还需要讲解说明下二叉排序树的删除结点操作。

当在二叉排序树中删除某个关键字时,不能把以该关键字所在的结点为根的子树都删除,而是只删除这一个结点,并保持二叉排序树的有序性。

加入要删除的结点为p,则删除结点p的过程分为如下三种情况:

  • 第一种情况:p结点为叶子结点。直接删除,不会破坏二叉排序树的有序性。

  • 第二种情况:p结点只有右子树而无左子树,或者只有左子树而无右子树(即被删除结点只有一棵子树的情况)。只需要将p结点的子树直接连接在原来p与双亲结点相连的指针上即可。

  • 第三种情况:p结点既有左子树又有右子树。其步骤如下:
    • 先沿着p结点的左子树根结点的右指针一直向右走,直到其右子树的最右边的一个结点r(也可以沿着p结点的右子树根结点的左指针一直向左走,直到来到其左子树的最左边的一个结点)。
    • 然后将p结点中的关键字用r中的关键字替代。
    • 最后判断r如果是叶子结点则按照第一种情况,如果r不是叶子结点,则按照第二种情况处理(此时的r不可能是有两个子树的结点)。

或者

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
二叉搜索树,也叫二叉查找树(BST,Binary Search Tree),是一种特殊的二叉树。相对于普通的二叉树,它有一个特点:所有左子树的节点都比根节点小,所有右子树的节点都比根节点大。因此,它可以快速地进行查找、插入、删除等操作。 具体来说,二叉搜索树的定义如下: - 节点的左子树中所有节点的值都小于该节点的值。 - 节点的右子树中所有节点的值都大于该节点的值。 - 左右子树也都是二叉搜索树。 如下图所示,就是一个二叉搜索树的例子: ``` 8 / \ 3 10 / \ \ 1 6 14 / \ / 4 7 13 ``` 在这个树中,每个节点都满足左子树的节点值小于该节点的值,右子树的节点值大于该节点的值。比如,节点 3 的左子树是 1 和 6,右子树是 4 和 7,都满足要求。 二叉搜索树的主要操作包括查找、插入和删除。 查找操作可以通过递归或者循环实现。递归实现如下: ``` def search(root, val): if not root or root.val == val: return root if root.val > val: return search(root.left, val) else: return search(root.right, val) ``` 插入操作需要先查找到插入的位置,然后创建一个新节点插入到该位置: ``` def insert(root, val): if not root: return TreeNode(val) if root.val > val: root.left = insert(root.left, val) else: root.right = insert(root.right, val) return root ``` 删除操作比较复杂,需要考虑多种情况。如果要删除的节点只有一个子节点,直接将其子节点替换上来即可。如果要删除的节点有两个子节点,可以找到其右子树中的最小节点(或者左子树中的最大节点)来替换该节点,然后再删除该最小节点(或者最大节点)。 ``` def delete(root, val): if not root: return None if root.val == val: if not root.left: return root.right elif not root.right: return root.left else: # 找到右子树中的最小节点 p = root.right while p.left: p = p.left root.val = p.val root.right = delete(root.right, p.val) elif root.val > val: root.left = delete(root.left, val) else: root.right = delete(root.right, val) return root ``` 需要注意的是,在删除节点时,要保证删除后的树仍然是二叉搜索树。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值