【玩转数据结构Part2】二分搜索树

总结

二分搜索树是一种特殊的二叉树,二分树的遍历一共分四种:前序遍历,中序遍历,后序遍历,层序遍历。四种遍历方式的主要区别是:节点的访问顺序不同。
层序遍历与先序、中序、后序遍历不同。层序遍历用到了队列,而先、中、后序需要用到栈。
因此,先、中、后序遍历 可以 采用递归方式来实现,而层序遍历则没有递归方式。

本博客以二分搜索树为例,分别从添加元素,查询元素,四种遍历方式,删除元素四个方面进行讲述。(一般的二叉树也是同理的,尤其是遍历操作,思路和代码是完全一样的)

二分搜索树

定义树的节点类:

class TreeNode:
    def __init__(self,item):
        self.val = item
        self.left = None
        self.right = None

一、添加元素

def add(self, root, elem): # 往二分搜索树中添加元素,并返回BST的根节点
    if not root:
        root = TreeNode(elem)
        self.size += 1
        return root
    if elem < root.val:
        root.left = self.add(root.left, elem)
    else:
        root.right = self.add(root.right, elem)
    return root

二、查询元素

def contains(self, root, elem): # 在BST中找寻元素elem
    if not root or not elem:
        return False
    if root.val == elem:
        return True
    elif elem < root.val:
        return self.contains(root.left, elem)
    else:
        return self.contains(root.right, elem)

三、遍历

可以使用递归的写法,也可以使用非递归的方法。前序遍历的递归和非递归写法都要掌握,中序和后序遍历只需掌握递归写法即可,非递归写法不仅复杂,而且实际应用不广。

1. 前序遍历(也叫深度优先遍历)

深度优先遍历的非递归写法要借助来实现。

def preorder(self, root): # 递归
    if not root:
        return
    print(root.val)
    self.preorder(root.left)
    self.preorder(root.right)

# 前序遍历的非递归写法,借助栈来实现,依次将右节点和左节点加入栈中
def preorder_2(self, root):
    if not root:
        return
    stack = [root]
    while stack:
        cur = stack.pop()
        print(cur.val)
        if cur.right:
            stack.append(cur.right)
        if cur.left:
            stack.append(cur.left)

2. 中序遍历

中序遍历二分搜索树,可以得到顺序序列。

def inorder(self, root):
    if not root:
        return
    self.inorder(root.left)
    print(root.val)
    self.inorder(root.right)

中序遍历的非递归写法:

def inorder(root):
    if not root:
        return
    res, stack = [], []
    while root or stack:
        while root:
            stack.append(root)
            root = root.left
        ## 此时的stack栈顶存放的是最左端的节点
        else:  # if stack:
            root = stack.pop()
            res.append(root)
            root = root.right
    return res

3. 后序遍历

def postorder(self, root):
    if not root:
        return
    self.postorder(root.left)
    self.postorder(root.right)
    print(root.val)

4. 层序遍历(广度优先遍历)

层序遍历的递归方式:将当前节点,当前所在的level以及结果数组传递给递归函数。在递归函数中,取出节点的值,添加到level参数对应结果数组元素中。

广度优先遍历的非递归写法要借助队列来实现。

def levelorder(self, root): # 层序遍历(广度优先遍历)的非递归实现
    if not root:
        return
    queue = [root]
    while queue:
        cur = queue.pop(0)
        print(cur.val)
        if cur.left:
            queue.append(cur.left)
        if cur.right:
            queue.append(cur.right)

四、删除元素

1. 删除BST的最小值和最大值

第一步, 我们先要获得BST的最大值或最小值(注意:最大值或最小值不一定是叶节点,例如下图)

def minimum(self, root): # 得到BST的最小值(注意,最小值不一定是叶节点)
     assert self.size > 0
     if not root.left:
         return root.val
     return self.minimum(root.left)

def maximum(self, root): # 得到BST的最大值
    assert self.size > 0
    if not root.right:
        return root.val
    return self.maximum(root.right)

第二步,删除最小值/最大值,如果最值是叶节点,那么很容易,直接删除即可,如果最值不是叶节点,还需要将其子树整体往上搬一级。

def removeMin(self, root):
    if not root.left: # 如果不含左子树,则root为最小节点,需要删除
        rightTree = root.right # 先将root的右子树保存下来
        root.right = None  # 释放root的right子树
        self.size -= 1
        return rightTree # 返回root的右子树即可(因为root没有左子树)
    # 如果包含左子树,则递归查找最小的元素
    root.left = self.removeMin(root.left) # 把root和左子树接上
    return root

def removeMax(self, root):
    if not root.right:
        leftTree = root.left
        root.left = None
        self.size -= 1
        return leftTree
    root.right = self.removeMax(root.right)
    return root

如果想要删除了最小节点之后,返回最小节点的值,可以将minimum()函数和removeMin()函数结合起来用:

def combine_min(self, root):
    min_value = self.minimum(root)
    self.removeMin(root)
    return min_value

2. 删除BST的任意节点

根据待删除节点的位置,可以分为如下四种情况:

  • 删除只有左孩子的节点
  • 删除只有右孩子的节点
  • 删除叶子节点
  • 删除左右都有孩子的节点(最复杂,是最一般的情况,包括了前三种)

例如,删除图中的节点14,需要将其左右子树融合起来。找14的后继节点(右子树中最小的节点,即15)来取代14,(同理,也可以使用14的前驱节点来替换14,即用12来替换,效果是一样的,都能保持BST的平衡)

#### 删除BST中任意元素(并用后继结点代替它)
def remove(self, root, elem):  # 删除元素elem对应的节点
    if not root or not elem:
        return
    if elem < root.val:
        root.left = self.remove(root.left, elem) # 在左子树中找elem,返回的子树要用root.left接住
    elif elem > root.val:
        root.right = self.remove(root.right, elem)
    else: # elem == root.val
        if not root.left: # 不含左子树时,直接将右子树返回即可
            return root.right
        elif not root.right:
            return root.left
        else:  # 既有左子树又有右子树
            successor = self.minimum(root.right) # 找到后继结点,先拿出来
            successor.right = self.removeMin(root.right) # 把原先的后继结点删除,并且把root的左右子树接到后继结点上
            successor.left = root.left
        return successor
    return root
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值