树-二叉搜索树Binary Search Tree, since 2022-05-17

(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

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

插入和删除

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

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

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

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

平衡搜索树Balanced Search Tree

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

使搜索树维持平衡的常用方法是旋转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 2022-05-17)