数据结构与算法(Python版)五十三:二叉查找树实现及算法分析

二叉搜索树的实现: BST.put方法

put(key, val)方法:插入key构造BST

首先看BST是否为空,如果一个节点都没有,那么key成为根节点root,否则,就调用一个递归函数_put(key, val, root)来放置key

二叉搜索树的实现: _put辅助方法

_put(key, val, currentNode)的流程

如果key比currentNode小,那么_put到左子树

  • 但如果没有左子树,那么key就成为左子节点

如果key比currentNode大,那么_put到右子树

  • 但如果没有右子树,那么key就成为右子节点
def _put(self, key, val, currentNode):
       # 递归左子树
       if key < currentNode.key:
           if currentNode.hasLeftChild():
               self._put(key, val, currentNode.leftChild)
           else:
               currentNode.leftChild = TreeNode(key, val, parent=currentNode)
       else:
           # 递归右子树
           if currentNode.hasRightChild():
               self._put(key, val, currentNode.rightChild)
           else:
               currentNode.rightChild = TreeNode(key, val, parent=currentNode)

二叉搜索树的实现: 索引赋值

随手把__setitem__做了

特殊方法(前后双下划线)

可以myZipTree[‘PKU’] = 100871

def __setitem__(self, k, v):
    self.put(k, v)

在这里插入图片描述

二叉搜索树的实现: BST.put图示

插入key=19, currentNode的变化过程(灰色) :

在这里插入图片描述

二叉搜索树的实现: BST.get方法

在树中找到key所在的节点取到payload

def get(self, key):
    if self.root:
        # 递归函数
        res = self._get(key, self.root)
        if res:
            # 找到节点
            return res.payload
        else:
            return None
    else:
        return None

def _get(self, key, currentNode):
    if not currentNode:
        return None
    elif currentNode.key == key:
        return currentNode
    elif key < currentNode.key:
        return self._get(key, currentNode.leftChild)
    else:
        return self._get(key, currentNode.rightChild)

二叉搜索树的实现: 索引和归属判断

__getitem__特殊方法

实现val= myZipTree[‘PKU’]

__contains__特殊方法

实现’PKU’ in myZipTree的归属判断运算符in

def __getitem__(self, key):
    return self.get(key)

def __contains__(self, key):
    if self._get(key, self.root):
        return True
    else:
        return False

在这里插入图片描述

二叉搜索树的实现:迭代器

我们可以用for循环枚举字典中的所有key

ADT Map也应该实现这样的迭代器功能

特殊方法__iter__可以用来实现for迭代

BST类中的__iter__方法直接调用了TreeNode中的同名方法
在这里插入图片描述

TreeNode类中的__iter__迭代器

迭代器函数中用了for迭代,实际上是递归函数,yield是对每次迭代的返回值中序遍历的迭代

def __iter__(self):
    if self:
        if self.hasLeftChild():
            for elem in self.leftChild:
                yield elem
        yield self.key
        if self.hasRightChild():
            for elem in self.rightChild:
                yield elem

二叉查找树的实现: BST.delete方法

有增就有减, 最复杂的delete方法:

用_get找到要删除的节点,然后调用remove来删除,找不到则提示错误

def delete(self, key):
    if self.size > 1:
        nodeToRemove = self._get(key, self.root)
        if nodeToRemove:
            self.remove(nodeToRemove)
            self.size = self-1
        else:
            raise KeyError('Error, key not in tree')
    elif self.size == 1 and self.root.key == key:
        self.root = None
        self.size = self.size - 1
    else:
        raise KeyError('Error, key not in tree')

__delitem__特殊方法

实现del myZipTree[‘PKU’]这样的语句操作

def __delitem__(self, key):
    self.delete(key)

在delete中, 最复杂的是找到key对应的节点之后的remove节点方法!

二叉查找树的实现: BST.remove方法

从BST中remove一个节点, 还要求仍然保持BST的性质, 分以下3种情形:

这个节点没有子节点
这个节点有1个子节点
这个节点有2个子节点

没有子节点的情况好办, 直接删除

if currentNode.isLeaf(): #leaf
    if currentNode == currentNode.parent.leftChild:
        currentNode.parent.leftChild = None
    else:
        currentNode.parent.rightChild = None

在这里插入图片描述

第2种情形稍复杂:被删节点有1个子节点

解决:将这个唯一的子节点上移,替换掉被删节点的位置
在这里插入图片描述

但替换操作需要区分几种情况:

被删节点的子节点是左?还是右子节点?
被删节点本身是其父节点的左?还是右子节点?
被删节点本身就是根节点?

else: # this node has one child
    if currentNode.hasLeftChild():
        if currentNode.isLeftChild():
            currentNode.leftChild.parent = currentNode.parent
            currentNode.parent.leftChild = currentNode.leftChild
        elif currentNode.isRightChild():
            currentNode.leftChild.parent = currentNode.parent
            currentNode.parent.rightChild = currentNode.leftChild
        else:
            currentNode.replaceNodeData(currentNode.leftChild.key,
                               currentNode.leftChild.payload,
                               currentNode.leftChild.leftChild,
                               currentNode.leftChild.rightChild)
    else:
        if currentNode.isLeftChild():
            currentNode.rightChild.parent = currentNode.parent
            currentNode.parent.leftChild = currentNode.rightChild
        elif currentNode.isRightChild():
            currentNode.rightChild.parent = currentNode.parent
            currentNode.parent.rightChild = currentNode.rightChild
        else:
            currentNode.replaceNodeData(currentNode.rightChild.key,
                               currentNode.rightChild.payload,
                               currentNode.rightChild.leftChild,
                               currentNode.rightChild.rightChild)

第3种情形最复杂:被删节点有2个子节点

这时无法简单地将某个子节点上移替换被删节点,但可以找到另一个合适的节点来替换被删节点,这个合适节点就是被删节点的下一个key值节点,即被删节点右子树中最小的那个,称为“后继”
在这里插入图片描述

BinarySearchTree类: remove方法(情形3)

elif currentNode.hasBothChildren(): #interior
    succ = currentNode.findSuccessor()
    succ.spliceOut()
    currentNode.key = succ.key
    currentNode.payload = succ.payload

TreeNode类:寻找后继节点

def findSuccessor(self):
    succ = None
    if self.hasRightChild():
        succ = self.rightChild.findMin()
    else:
        if self.parent:
            if self.isLeftChild():
                succ = self.parent
            else:
                self.parent.rightChild = None
                succ = self.parent.findSuccessor()
                self.parent.rightChild = self
    return succ
    
def findMin(self):
    current = self
    while current.hasLeftChild():
        current = current.leftChild
    return current

TreeNode类:摘出节点spliceOut()

def spliceOut(self):
    if self.isLeaf():
        if self.isLeftChild():
            self.parent.leftChild = None
        else:
            self.parent.rightChild = None
    elif self.hasAnyChildren():
        if self.hasLeftChild():
            if self.isLeftChild():
                self.parent.leftChild = self.leftChild
            else:
                self.parent.rightChild = self.leftChild
            self.leftChild.parent = self.parent
        else:
            if self.isLeftChild():
                self.parent.leftChild = self.rightChild
            else:
                self.parent.rightChild = self.rightChild
            self.rightChild.parent = self.parent

二叉查找树:算法分析(以put为例)

其性能决定因素在于二叉搜索树的高度(最大层次) , 而其高度又受数据项key插入顺序的影响。

如果key的列表是随机分布的话, 那么大于和小于根节点key的键值大致相等

BST的高度就是log2n(n是节点的个数) ,而且, 这样的树就是平衡树

put方法最差性能为O(log2n)。

但key列表分布极端情况就完全不同

按照从小到大顺序插入的话,如下图这时候put方法的性能为O(n),其它方法也是类似情况
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值