数据结构和算法-二叉查找树

二叉查找树(Binary Search Tree), 简称BST,必须具有以下性质:

  • 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根结点的值
  • 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根结点的值
  • 任意节点的左、右子树也分别为二叉查找树
  • 没有键值相等的节点

在二叉查找树中查找节点时, 平均运行时间为O(logn)(平衡情况), 最坏为O(n)(极度不平衡), 平均深度是O(logn)

在有序数组中使用二分查找时最坏的时间复杂度是O(logn), 但是二叉搜索树的插入删除的性能更优

二叉搜索树数组对比

- 数组 二叉搜索树
查找 O(logn)(二分查找) O(logn)
插入 O(n) O(logn)
删除 O(n) O(logn)

实现

  • 插入
  • 查找
  • 删除
    • 要删除的节点是叶子节点, 只需要让父节点指向null
    • 要删除的节点是只有一个子节点(不管是左/右), 需要让父节点指向子节点
    • 要删除的节点有两个子节点, 则需要找到该节点右子树中的最小节点, 然后替换到该节点, 并且再删除这个最小节点

特点

  • 快速的插入/查找/删除
  • 快速找到最大/最小节点
  • 中序遍历可以得到有序的数据, O(n)
# coding:utf-8


class TreeNode(object):
    def __init__(self, key, value, parent=None, left=None, right=None):
        self.key = key
        self.value = value
        self.parent = parent
        self.left = left
        self.right = right

    def isLeftChild(self):
        return self.parent and self.parent.left == self

    def isRightChild(self):
        return self.parent and self.parent.right == self

    def childrenNums(self):
        num = 0
        if self.left:
            num += 1
        if self.right:
            num += 1
        return num


class BinarySearchTree(object):
    def __init__(self):
        self.root = None
        self.size = 0

    def put(self, key, value):
        if self.root:
            self._put(key, value, self.root)
        else:
            self.root = TreeNode(key, value)
        self.size += 1

    def _put(self, key, value, current):
        if key < current.key:
            if current.left:
                self._put(key, value, current.left)
            else:
                current.left = TreeNode(key, value, parent=current)
        elif key > current.key:
            if current.right:
                self._put(key, value, current.right)
            else:
                current.right = TreeNode(key, value, parent=current)

    def get(self, key):
        if self.root:
            res = self._get(key, self.root)
        else:
            return None
        return res

    def _get(self, key, current):
        if not current:
            return None
        if key < current.key:
            return self._get(key, current.left)
        elif key > current.key:
            return self._get(key, current.right)
        else:
            return current

    def delete(self, key):
        if self.size > 1:
            # 要先找到该节点
            node2remove = self.get(key)
            if node2remove:
                self.remove(node2remove)
                self.size -= 1
            else:
                raise Exception('no element')
        elif self.size == 1 and self.root.key == key:
            self.root = None
            self.size -= 1
        else:
            raise Exception('no element')

    def remove(self, current):
        childrens = current.childrenNums()
        if 0 == childrens:
            if current.isLeftChild():
                current.parent.left = None
            else:
                current.parent.right = None
        elif 1 == childrens:
            # 如果该节点有左子树
            if current.left:
                if current.isLeftChild():
                    current.left.parent = current.parent
                    current.parent.left = current.left
                elif current.isRightChild():
                    current.left.parent = current.parent
                    current.parent.right = current.left
                else:
                    self.root = current.left
            # 如果是右子树
            elif current.right:
                if current.isLeftChild():
                    current.right.parent = current.parent
                    current.parent.left = current.right
                elif current.isRightChild():
                    current.right.parent = current.parent
                    current.parent.right = current.right
                else:
                    self.root = current.right
        # 如果有两个子节点
        else:
            parent = current
            minChild = current.right
            while minChild.left != None:
                parent = minChild
                minChild = minChild.left
            current.key = minChild.key
            current.value = minChild.value
            # 注意以下情况判断, 因为有的没有左子节点
            if parent.left == minChild:
                parent.left = minChild.right
                if minChild.right:
                    minChild.right.parent = parent
            else:
                parent.right = minChild.right
                if minChild.right:
                    minChild.right.parent = parent

    def length(self):
        return self.size

    def __setitem__(self, key, value):
        self.put(key, value)

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

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

    def mid(self, root):
        if not root:
            return
        self.mid(root.left)
        print(root.value)
        self.mid(root.right)



if __name__ == '__main__':
    mytree = BinarySearchTree()
    mytree[7] = "7"
    mytree[2] = "2"
    mytree[11] = "11"
    mytree[8] = "8"
    mytree[19] = "19"
    mytree[5] = "5"
    mytree[1] = "1"

    # 中序遍历
    mytree.mid(mytree.root)
    print('------')

    # 删除叶子节点1
    del mytree[1]
    mytree.mid(mytree.root)
    print('------')

    # 删除有一个子节点的2
    del mytree[2]
    mytree.mid(mytree.root)
    print('------')

    # 删除有两个子节点的11
    del mytree[11]
    mytree.mid(mytree.root)

时间复杂度

  • 最坏: 退化成链表 O(n)
  • 最好: 平衡 O(logn)

问题

散列表更加高效, 为什么还有二叉查找树?

  • 散列表的数据时无序存储的, 如果要输出有序数据的话还需要额外进行排序. 但是二叉查找树可以直接中序遍历(O(n))
  • 散列表扩容耗时长, 遇到散列冲突时性能不稳定. 工程中用的AVL树性能稳定
  • 散列表构造复杂, 需要考虑散列函数, 冲突解决等. 而二叉查找树仅仅考虑平衡问题

资料

  • < <大话数据结构> >
  • 二叉树
  • < <数据结构和算法> >

猜你喜欢

转载自www.cnblogs.com/zlone/p/11073558.html