美文网首页
树-二叉搜索树Binary Search Tree, since

树-二叉搜索树Binary Search Tree, since

作者: Mc杰夫 | 来源:发表于2022-05-17 16:25 被阅读0次

    (2022.05.17 Tues)
    前面提到的二叉树的应用之一是搜索树。我们这里用树结构实现顺序映射/有序映射(sorted map),其中的三个基础方法是

    • M[k]:返回映射中key k的关联值;用__getitem__方法实现;如果没有则返回KeyError错误
    • M[k] = p:对映射中key k的关联值赋值,如果不存在则新建,如果存在则覆盖;用__setitem__方法实现
    • del M[k]:删除映射中的key k和关联值

    假定存入树的的key按序排列,则二叉树是实现映射的良好数据结构。二叉搜索树满足下面条件,当位置p保存了键值对(k, v)

    • p左子树的key小于k
    • p右子树的key大于k

    对二叉搜索树做中序遍历,遍历结果按单调顺序排列。

    (2022.05.18 Wed)

    搜索

    在二叉搜索树中执行搜索,对于给定的key k,从根节点root开始,比较root点的key r

    • 如果k > r,则root的右子树与k做比较
    • 如果k < 'r',则root的左子树与k作比较
    • 如果k = r,则root就是索要找的点,返回
    • 如果左/右子树为空,则key k不在该二叉搜索树中,返回搜索k得到的最后一个节点p

    复杂度分析:搜索步数和搜索树的最大高度有关,复杂度是O(h)。最坏的情况,该二叉树只有一个叶节点,其余节点的度都为1,则这样的搜索将会遍历每一个节点,最坏情况复杂度变为O(n)

    插入和删除

    插入相对删除比较简单。插入(k, v)到一个搜索二叉树上,首先确定需要插入的值不在二叉树上。如果在该树上,则直接修改对应节点的值。如果不在该二叉树上,根据前面的搜索算法找到节点p,如果p点对应的key k_pk_p > k,则(k, v)加在p的左子树上,否则加在右子树上。插入操作总是发生在路径底端。

    插入的复杂度包括两部分,一部分是搜索(O(h)),另一部分是加子树(O(1)),总复杂度是O(h)

    删除操作略复杂。首先执行搜索操作,找到需要删除的节点p。该节点有一下两种情况

    • 该节点p没有子树,则直接删除
    • 该节点有一个子节点,删除之后除了子节点外其他关系不做变化,子节点挪到该节点所在位置代替
    • 该节点p有两个子节点,删除p之后会空出一个位置。从p的左子树中找到key最大的点k,该点是所有小于p的节点里最大的点。将k点移动到p的点上。此时k点的位置控制。考虑到k点是小于p的节点里最大的,所以k点没有右节点,于是k点子树的处理可以按照上面的一个子节点的情况来处理。

    平衡搜索树Balanced Search Tree

    上面介绍的二叉搜索树最坏的情况是整棵树只有一个leaf,而每个中间节点只有一个子树。对这棵树做检索操作复杂度达到O(n)。需要对该树进行处理使其成为一个检索复杂度保持在O(logn)

    使搜索树维持平衡的常用方法是旋转rotation,即子树通过旋转到其parent上成为parent,而原来的parent成为child。

    搜索二叉树的实现

    from collections import MutableMapping
    class MapBase(MutableMapping):
        class _Item:
            __slots__ = '_key', '_value'
            def __init__(self, k, v):
                self._key = k
                self._value = v
            def __eq__(self, other):
                return self._key == other._key
            def __ne__(self, other):
                return not (self == other)
            def __lt__(self, other):
                return self._key < other._key
            def key(self):
                return self._key
            def value(self):
                return self._value
        def __len__(self, *args):
            return super().__len__(*args)
    
    class TreeMap(LinkedBinaryTree, MapBase):
        def _subtree_search(self, p, k):
            if k == p.key():
                return p
            elif k < p.key():
                if self.left(p) is not None:
                    return self._subtree_search(self.left(p), k)
            else:
                if self.right(p) is not None:
                    return self._subtree_search(self.right(p), k)
            return p
        def _subtree_first_position(self, p):
            '''Return Position of first item in subtree rooted at p.'''
            walk = p
            while self.left(walk) is not None: # keep walking left
                walk = self.left(walk)
            return walk
        def _subtree_last_position(self, p):
            '''Return Position of last item in subtree rooted at p.'''
            walk = p
            while self.right(walk) is not None: # keep walking right
                walk = self.right(walk)
            return walk
        def first(self):
            """Return the first Position in the tree (or None if empty)."""
            return self._subtree_first_position(self.root()) if len(self) > 0 else None
        def last(self):
            """Return the last Position in the tree (or None if empty)."""
            return self._subtree_last_position(self.root()) if len(self) > 0 else None
        def before(self, p):
            """Return the Position just before p in the natural order.
            Return None if p is the first position."""
            if self.left(p):
                return self._subtree_last_position(self.left(p))
            else:
                # walk upward
                walk = p
                above = self.parent(walk)
                while above is not None and walk == self.left(above):
                    walk = above
                    above = self.parent(walk)
                return above
        def after(self, p):
            """Return the Position just after p in the natural order.
            Return None if p is the last position."""
            # symmetric to before(p)
        def find_min(self):
            """Return (key,value) pair with minimum key (or None if empty)."""
            if self.is_empty():
                return None
            else:
                p = self.first()
            return (p.key(), p.value())
        def __getitem__(self, k):
            """Return value associated with key k (raise KeyError if not found)."""
            if self.is empty( ):
                raise KeyError("Key Error: "+ repr(k))
            else:
                p = self._subtree_search(self.root(), k)
                # self._rebalance_access(p) # hook for balanced tree subclasses
                if k != p.key():
                    raise KeyError("Key Error: "+ repr(k))
            return p.value()
        def __setitem__(self, k, v):
            """Assign value v to key k, overwriting existing value if present."""
            if self.is empty():
                leaf = self._add_root(self._Item(k,v)) # from LinkedBinaryTree
            else:
                p = self._subtree_search(self.root(), k)
                if p.key() == k:
                    p.element()._value = v # replace existing item’s value
                    self._rebalance_access(p) # hook for balanced tree subclasses
                    return
                else:
                    item = self._Item(k,v)
                    if p.key() < k:
                        leaf = self._add_right(p, item) # inherited from LinkedBinaryTree
                    else:
                        leaf = self._add_left(p, item) # inherited from LinkedBinaryTree
            # self._rebalance_insert(leaf)
        def __iter__(self):
            """Generate an iteration of all keys in the map in order."""
            p = self.first()
            while p is not None:
                yield p.key( )
                p = self.after(p)
        def delete(self, p):
            """Remove the item at given Position."""
            # self. validate(p) # inherited from LinkedBinaryTree
            if self.left(p) and self.right(p): # p has two children
                replacement = self._subtree_last_position(self.left(p))
                self. replace(p, replacement.element( )) # from LinkedBinaryTree
                p = replacement
            # now p has at most one child
            parent = self.parent(p)
            self._delete(p) # inherited from LinkedBinaryTree
            self. rebalance delete(parent) # if root deleted, parent is None
        def __delitem__(self, k):
            """Remove item associated with key k (raise KeyError if not found)."""
            if not self.is_empty():
                p = self. subtree search(self.root(), k)
                if k == p.key():
                    self.delete(p) # rely on positional version
                    return # successful deletion complete
                # self._rebalance_access(p) # hook for balanced tree subclasses
            raise KeyError("Key Error: "+ repr(k))
    

    Reference

    1 Goodrich and etc., Data Structures and Algorithms in Python

    相关文章

      网友评论

          本文标题:树-二叉搜索树Binary Search Tree, since

          本文链接:https://www.haomeiwen.com/subject/cqsnurtx.html