數據結構和算法-二叉查找樹

二叉查找樹(Binary Search Tree), 簡稱BST,必須具備如下性質:node

  • 若任意節點的左子樹不空,則左子樹上全部節點的值均小於它的根結點的值
  • 若任意節點的右子樹不空,則右子樹上全部節點的值均大於它的根結點的值
  • 任意節點的左、右子樹也分別爲二叉查找樹
  • 沒有鍵值相等的節點

在二叉查找樹中查找節點時, 平均運行時間爲O(logn)(平衡狀況), 最壞爲O(n)(極度不平衡), 平均深度是O(logn)python

在有序數組中使用二分查找時最壞的時間複雜度是O(logn), 可是二叉搜索樹的插入刪除的性能更優git

二叉搜索樹數組對比github

- 數組 二叉搜索樹
查找 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樹性能穩定
  • 散列表構造複雜, 須要考慮散列函數, 衝突解決等. 而二叉查找樹僅僅考慮平衡問題

資料

  • < <大話數據結構> >
  • 二叉樹
  • < <數據結構和算法> >

相關文章
相關標籤/搜索