[DS] 二叉搜索树操作集


二叉树搜索树 Binary Search Tree 的各种操作的 Python 实现

Posted by Yufan Zheng on May 18, 2018

闲来无事,整理一下二叉搜索树的各种操作全集吧。以后准备面试的时候,也可以有个参考,不用到处找了。

BST Model

树的节点模型,和二叉树模型。

class TNode(object):

    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

    def __repr__(self):
        return str(self.value)


class BTree(object):

    def __init__(self, root: TNode = None):
        self.root = root
...

树的节点包括,左节点和右节点,还有存储在节点的值大小。树有唯一的 member variable,就是根节点。

Tree Traversal

前序遍历

...
    def preOrder(self):
        if self.root is None:
            return
        stack = [self.root]
        while stack:
            node = stack.pop()
            print(node, end=' ')
            if node.right:
                stack.append(node.right)
            if node.left:
                stack.append(node.left)
        print()

    def preOrderRec(self):
        root = self.root
        self._preOrderRec(root)
        print()

    def _preOrderRec(self, root: TNode):
        if root:
            print(root, end=' ')
            self._preOrderRec(root.left)
            self._preOrderRec(root.right)
...

中序遍历

...
    def inOrder(self):
        if self.root is None:
            return
        node, stack = self.root, []
        while node or stack:
            while node:
                stack.append(node)
                node = node.left
            if stack:
                node = stack.pop()
                print(node, end=' ')
                node = node.right

    def inOrderRec(self):
        root = self.root
        self._inOrderRec(root)
        print()

    def _inOrderRec(self, root: TNode):
        if root:
            self._inOrderRec(root.left)
            print(root, end=' ')
            self._inOrderRec(root.right)       
...

后序遍历

...
    def postOrder(self):
        if self.root is None:
            return
        prev, stack = None, [self.root]
        while stack:
            curr = stack[-1]
            # 3 conditions to visit current node
            #   - Current node has no child
            #   - Current node's right child has just been visited
            #   - Current node's left child has just been visited and right child is None
            if curr.left is None and curr.right is None or \
                prev is not None and curr.right == prev or \
                prev is not None and curr.left == prev and curr.right is None:
                node = stack.pop()
                prev = node
                print(node, end=' ')
            else:
                if curr.right:
                    stack.append(curr.right)
                if curr.left:
                    stack.append(curr.left)
        print()

    def postOrderRec(self):
        root = self.root
        self._postOrderRec(root)
        print()

    def _postOrderRec(self, root: TNode):
        if root:
            self._postOrderRec(root.left)
            self._postOrderRec(root.right)
            print(root, end=' ')
...

层级遍历

...
    def levelOrder(self):
        if self.root is None:
            return
        currLevel, nextLevel = [self.root], []
        while currLevel:
            node = currLevel.pop()
            print(node, end=' ')
            if node.right:
                nextLevel.append(node.right)
            if node.left:
                nextLevel.append(node.left)
            if len(currLevel) == 0:
                currLevel, nextLevel = nextLevel, currLevel
                print()
...

Insert / Delete / Search

插入节点

...
    def insert(self, node: TNode):
        if node is None:
            return
        self.root = self._insertRec(self.root, node)

    def _insertRec(self, root: TNode, node: TNode):
        if root is None or root.value == node.value:
            return node
        if root.value > node.value:
            root.left = self._insertRec(root.left, node)
        if root.value < node.value:
            root.right = self._insertRec(root.right, node)
        return root
...

删除节点

...
    def delete(self, node: TNode):
        if self.root is None or node is None:
            return
        self._delete(self.root, node)

    def _delete(self, root: TNode, node: TNode):
        if root is None:
            raise Exception('Try to delete a non-existing value.')
        if root.value > node.value:
            root.left = self._delete(root.left, node)
        elif root.value < node.value:
            root.right = self._delete(root.right, node)
        else:
            if root.left is None:
                root = root.right
            elif root.right is None:
                root = root.left
            else:
                minValueNode = self.minValueNode(root.right)
                root.value = minValueNode.value
                root.right = self._delete(root.right, minValueNode)
        return root

    def minValueNode(self, root: TNode):
        if root is None:
            return root
        while root.left:
            root = root.left
        return root             
...

查找节点

...
    def search(self, node: TNode):
        return self._search(self.root, node)

    def _search(self, root: TNode, node: TNode):
        if root is None or node is None:
            return False
        if root.value == node.value:
            return True
        if root.value > node.value:
            return self._search(root.left, node)
        if root.value < node.value:
            return self._search(root.right, node)
...

Node Path / Lowest Common Ancestor

到节点的路径

...
    def pathToNode(self, node: TNode):
        if node is not None:
            return self._pathToNode(self.root, node, [self.root])

    def _pathToNode(self, root: TNode, node: TNode, path: list):
        if root is None:
            return
        if root.value == node.value:
            return path
        if root.value > node.value:
            newpath = self._pathToNode(root.left, node, path + [root.left])
            if newpath is not None:
                return newpath
        if root.value < node.value:
            newpath = self._pathToNode(root.right, node, path + [root.right])
            if newpath is not None:
                return newpath
...

两个节点的最低的共同祖先

...
    def lowestCommonAncestor(self, node1: TNode, node2: TNode):
        path1 = self.pathToNode(node1)
        path2 = self.pathToNode(node2)
        if path1 is None or path2 is None:
            return
        index = 0
        while index < len(path1) and index < len(path2):
            if path1[index] != path2[index]:
                break
            index += 1
        return path1[index-1]
...

Heights / Check Balance

最小高度

...
    def minHeight(self):
        if self.root is None:
            return 0
        return self._minHeight(self.root, 1)

    def _minHeight(self, root: TNode, currHeight):
        if root.left is None or root.right is None:
            return currHeight
        return min(self._minHeight(root.left, currHeight+1),
                   self._minHeight(root.right, currHeight+1))
...

最大高度

...
    def maxHeight(self):
        if self.root is None:
            return 0
        return self._maxHeight(self.root, 0)

    def _maxHeight(self, root: TNode, currHeight):
        if root is None:
            return currHeight
        if root.left is None:
            return self._maxHeight(root.right, currHeight+1)
        if root.right is None:
            return self._maxHeight(root.left, currHeight+1)
        return max(self._maxHeight(root.left, currHeight+1),
                   self._maxHeight(root.right, currHeight+1))
...

判断树是否平衡

...
    def isBalance(self):
        if self.maxHeight() - self.minHeight() <= 1:
            return True
        return False
...

Build Tree From Two Traversal Lists

哪些情况可以构建出一个二叉树来

  • 前序 + 中序 -- 可以
  • 后序 + 中序 -- 可以
  • 前序 + 后序 -- 不可以

前序 + 中序构建二叉树

...
    def fromPreOrderAndInOrder(self, preorder: list, inorder: list):
        self.root = self._fromPreOrderAndInOrder(preorder, inorder)

    def _fromPreOrderAndInOrder(self, preorder: list, inorder: list):
        if inorder:
            idx = inorder.index(preorder.pop(0))
            root = TNode(inorder[idx])
            root.left = self._fromPreOrderAndInOrder(preorder, inorder[:idx])
            root.right = self._fromPreOrderAndInOrder(preorder, inorder[idx+1:])
            return root
...

后序 + 中序构建二叉树

...
    def fromInOrderAndPostOrder(self, inorder: list, postorder: list):
        self.root = self._fromPreOrderAndInOrder(inorder, postorder)

    def _fromInOrderAndPostOrder(self, inorder: list, postorder: list):
        if inorder:
            idx = inorder.index(postorder.pop())
            root = TNode(inorder[idx])
            root.right = self._fromInOrderAndPostOrder(inorder[:idx], postorder)
            root.right = self._fromInOrderAndPostOrder(inorder[idx+1:], postorder)
            return root
...

Binary Search Tree -> Sorted Double Linked List

将二叉搜索树转换成排序好了的双向链表

...
class ListNode(object):

    def __init__(self, value, pre=None, nxt=None):
        self.value = value
        self.pre = pre
        self.nxt = nxt

    def __repr__(self):
        return str(self.value)
...
class BTree(object):
...
    def toDoubleLinkedList(self):
        self._head, self._prev = None, None
        self._inorderConvert(self.root)
        return self._head

    _head, _prev = None, None

    def _inorderConvert(self, root: TNode):
        if root:
            self._inorderConvert(root.left)
            # Do some stuff here
            if self._head is None:
                self._head = self._prev = ListNode(root.value)
            else:
                if self._head == self._prev:
                    self._head.nxt = root
                    self._prev = root
                    self._prev.pre = self._head
                else:
                    self._prev.nxt = root
                    tmp = self._prev
                    self._prev = root
                    self._prev.pre = tmp
            self._inorderConvert(root.right)
...

At The End

这是我之前在准备面试的时候,刷数据结构,还有刷各种 LeetCode 的题目时,写下的代码,今天整理了一下。

完整代码的连接在这里。

还有一个更全面的 Python Data Structure / Algorithms 的合集,在这个地方。喜欢的话,可以给个 star 哦。