zoukankan      html  css  js  c++  java
  • 数据结构和算法-二叉查找树

    二叉查找树(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树性能稳定
    • 散列表构造复杂, 需要考虑散列函数, 冲突解决等. 而二叉查找树仅仅考虑平衡问题

    资料

    • <<大话数据结构>>
    • 二叉树
    • <<数据结构和算法>>
  • 相关阅读:
    【BZOJ 2820】 YY的GCD (莫比乌斯+分块)
    【BZOJ 2005】[Noi2010]能量采集 (容斥原理| 欧拉筛+ 分块)
    【HDU 4898】 The Revenge of the Princess’ Knight (后缀数组+二分+贪心+...)
    【BZOJ 3473】 字符串 (后缀数组+RMQ+二分 | 广义SAM)
    【BZOJ3439】 Kpm的MC密码 (TRIE+主席树)
    【GDOI2014 DAY2】Beyond (扩展KMP)
    【转】【最大子矩阵问题】【悬线法】 学习笔记
    【HDU4333】Revolving Digits(扩展KMP+KMP)
    【转】各种字符串算法大总结
    【poj2478-Farey Sequence】递推求欧拉函数-欧拉函数的几个性质和推论
  • 原文地址:https://www.cnblogs.com/zlone/p/11073558.html
Copyright © 2011-2022 走看看