数据结构——二叉查找树

2019-01-05

1 基础概念

定义:又称为二叉搜索树,设$x$为二叉查找树的一个节点,$x$节点的值为$x.val$。如果$y$$x$的左子树中的一个节点,则$y.val<=x.val$;如果$y$$x$的右子树中的一个节点,则$y.val>=x.val$

二叉查找树

特点

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

2 代码实现

2.1 节点定义

class BinarySearchTree(object):
    def __init__(self, key):
        self.__key = key
        self.__leftChild = None
        self.__rightChild = None
        self.__parent = None  # 父节点

2.2 查找

2.2.1 递归版本

# 查找 key值
def find(self, data):
    if data == self.getKey():
        return self
    elif data < self.getKey() and self.getLeftChild():
        return self.getLeftChild().find(data)
    elif data > self.getKey() and self.getRightChild():
        return self.getRightChild().find(data)
    else:
        return None

2.2.2 非递归版本

def find1(self, data):
    while data != self.getKey():
        if data < self.getKey():
            self = self.getLeftChild()
        else:
            self = self.getRightChild()
    return self

2.3 获取最大最小结点

# 返回树中的最小元素与最大元素的结点
def findMin(self):
    if self.getLeftChild():
        return self.getLeftChild().findMin()
    else:
        return self

def findMax(self):
    tree = self
    if tree:
        while tree.getRightChild():
            tree = tree.getRightChild()
    return tree

2.4 前驱和后继

  • 节点的前驱:是该节点的左子树中的最大节点。
  • 节点的后继:是该节点的右子树中的最小节点。
# 前驱和后继节点
def predecessor(self):
    curNode = self
    # 如果 curNode存在左孩子,则 "curNode的前驱结点"为"以其左孩子为根的子树的最大结点"
    if curNode.getLeftChild():
        return curNode.getLeftChild().findMax()
    # 如果 curNode没有左孩子。则 curNode有以下两种可能:
    # (1) curNode是"一个右孩子",则 "curNode的前驱结点"为 "它的父结点"
    # (2) curNode是"一个左孩子",则查找 "curNode的最低的父结点,并且该父结点要具有右孩子",
    #     找到的这个"最低的父结点"就是 "curNode的前驱结点"
    preNode = curNode.getParent()
    while preNode and curNode == preNode.getLeftChild():
        curNode = preNode
        preNode = preNode.getParent()
    return preNode

def successor(self):
    curNode = self
    # 如果 curNode存在右孩子,则 "curNode的后继结点"为"以其右孩子为根的子树的最小结点"
    if curNode.getRightChild():
        return curNode.getRightChild().findMin()
    # 如果 curNode没有右孩子。则 curNode有以下两种可能:
    # (1) curNode是"一个左孩子",则 "curNode的后继结点"为"它的父结点"
    # (2) curNode是"一个右孩子",则查找 "curNode的最低的父结点,并且该父结点要具有左孩子",
    #     找到的这个"最低的父结点"就是 "curNode的后继结点"
    sucNode = curNode.getParent()
    while sucNode and curNode == sucNode.getRightChild():
        curNode = sucNode
        sucNode = sucNode.getParent()
    return sucNode

2.5 插入

# 为了将 data插入到树 Tree中,先用find查找,如果找到 data,则什么也不做。
# 否则,将 data插入到遍历路径的最后一点。
def insert(self, data):
    if data < self.getKey():
        if self.getLeftChild():
            self.getLeftChild().insert(data)
        else:
            tree = BinarySearchTree(data)
            tree.setParent(self)
            self.setLeftChild(tree)
    elif data > self.getKey():
        if self.getRightChild():
            self.getRightChild().insert(data)
        else:
            tree = BinarySearchTree(data)
            tree.setParent(self)
            self.setRightChild(tree)

2.6 删除

删除某节点有3种情况:

  1. 如果节点是一片树叶,那么可以立即被删除。 删除1
  2. 如果节点只有一个儿子,则将此节点parent的指针指向此节点的儿子,然后删除。 删除2
  3. 如果节点有两个儿子,则将其右子树的最小数据代替此节点的数据,并将其右子树 的最小数据(不可能有左儿子,只有一个右儿子)删除。 删除3
def delete(self, data):
    if self is None:
        return self
    if data < self.getKey():
        self.setLeftChild(self.getLeftChild().delete(data))
    elif data > self.getKey():
        self.setRightChild(self.getRightChild().delete(data))
    elif self.getLeftChild() and self.getRightChild():
        data_min = self.getRightChild().findMin().getKey()
        self.setKey(data_min)
        self.setRightChild(self.getRightChild().delete(data_min))
    elif self.getLeftChild():
        self = self.getLeftChild()
    elif self.getRightChild():
        self = self.getRightChild()
    else:
        self = None
    return self

2.7 完整实现

# 实现二叉查找树
# -*- coding: utf-8 -*-

class BinarySearchTree(object):
    def __init__(self, key):
        self.__key = key
        self.__leftChild = None
        self.__rightChild = None
        self.__parent = None

    def getKey(self):
        return self.__key

    def setKey(self, data):
        self.__key = data

    def setLeftChild(self, tree):
        self.__leftChild = tree

    def getLeftChild(self):
        return self.__leftChild

    def setRightChild(self, tree):
        self.__rightChild = tree

    def getRightChild(self):
        return self.__rightChild

    def setParent(self, tree):
        self.__parent = tree

    def getParent(self):
        return self.__parent

    # 查找key值
    def find(self, data):
        if data == self.getKey():
            return self
        elif data < self.getKey() and self.getLeftChild():
            return self.getLeftChild().find(data)
        elif data > self.getKey() and self.getRightChild():
            return self.getRightChild().find(data)
        else:
            return None

    def find1(self, data):
        while data != self.getKey():
            if data < self.getKey():
                self = self.getLeftChild()
            else:
                self = self.getRightChild()
        return self

    # 返回树中的最小元素与最大元素的结点
    def findMin(self):
        if self.getLeftChild():
            return self.getLeftChild().findMin()
        else:
            return self

    def findMax(self):
        tree = self
        if tree:
            while tree.getRightChild():
                tree = tree.getRightChild()
        return tree

    # 前驱和后继节点
    def predecessor(self):
        curNode = self
        # 如果curNode存在左孩子,则"curNode的前驱结点"为"以其左孩子为根的子树的最大结点"
        if curNode.getLeftChild():
            return curNode.getLeftChild().findMax()
        # 如果curNode没有左孩子。则curNode有以下两种可能:
        # (1) curNode是"一个右孩子",则"curNode的前驱结点"为 "它的父结点"
        # (2) curNode是"一个左孩子",则查找"curNode的最低的父结点,并且该父结点要具有右孩子",
        #     找到的这个"最低的父结点"就是"curNode的前驱结点"
        preNode = curNode.getParent()
        while preNode and curNode == preNode.getLeftChild():
            curNode = preNode
            preNode = preNode.getParent()
        return preNode

    def successor(self):
        curNode = self
        # 如果curNode存在右孩子,则"curNode的后继结点"为"以其右孩子为根的子树的最小结点"
        if curNode.getRightChild():
            return curNode.getRightChild().findMin()
        # 如果curNode没有右孩子。则curNode有以下两种可能:
        # (1) curNode是"一个左孩子",则"curNode的后继结点"为"它的父结点"
        # (2) curNode是"一个右孩子",则查找"curNode的最低的父结点,并且该父结点要具有左孩子",
        #     找到的这个"最低的父结点"就是"curNode的后继结点"
        sucNode = curNode.getParent()
        while sucNode and curNode == sucNode.getRightChild():
            curNode = sucNode
            sucNode = sucNode.getParent()
        return sucNode

    # 为了将data插入到树Tree中,先用find查找,如果找到data,则什么也不做。否则,将data插入到遍历路径的最后一点。
    def insert(self, data):
        if data < self.getKey():
            if self.getLeftChild():
                self.getLeftChild().insert(data)
            else:
                tree = BinarySearchTree(data)
                tree.setParent(self)
                self.setLeftChild(tree)
        elif data > self.getKey():
            if self.getRightChild():
                self.getRightChild().insert(data)
            else:
                tree = BinarySearchTree(data)
                tree.setParent(self)
                self.setRightChild(tree)

    # 删除某节点有3种情况:
    # 1.如果节点是一片树叶,那么可以立即被删除。
    # 2.如果节点只有一个儿子,则将此节点parent的指针指向此节点的儿子,然后删除。
    # 3.如果节点有两个儿子,则将其右子树的最小数据代替此节点的数据,并将其右子树
    # 的最小数据(不可能有左儿子,只有一个右儿子)删除。
    def delete(self, data):
        if self is None:
            return self
        if data < self.getKey():
            self.setLeftChild(self.getLeftChild().delete(data))
        elif data > self.getKey():
            self.setRightChild(self.getRightChild().delete(data))
        elif self.getLeftChild() and self.getRightChild():
            data_min = self.getRightChild().findMin().getKey()
            self.setKey(data_min)
            self.setRightChild(self.getRightChild().delete(data_min))
        elif self.getLeftChild():
            self = self.getLeftChild()
        elif self.getRightChild():
            self = self.getRightChild()
        else:
            self = None
        return self

    # 树的先序遍历
    def preorder(self, nodelist = None):
        if nodelist is None:
            nodelist = []
        if self:
            nodelist.append(self.getKey())
            if self.getLeftChild():
                self.getLeftChild().preorder(nodelist)
            if self.getRightChild():
                self.getRightChild().preorder(nodelist)
        return nodelist

if __name__ == '__main__':
    tree = BinarySearchTree(17)
    tree.insert(5)
    tree.insert(35)
    tree.insert(2)
    tree.insert(29)
    tree.insert(11)
    node = tree.preorder()
    print('先序遍历:',node)
    print('树的最小结点值:',tree.findMin().getKey())
    print('树的最大结点值:',tree.findMax().getKey())
    print('查找节点操作')
    print(tree.find(2).getKey())
    print(tree.find1(2).getKey())
    print('查找节点前驱和后继结点操作')
    print(tree.find(5).predecessor().getKey())
    print(tree.find1(11).successor().getKey())
    print('删除节点操作')
    t = tree.delete(5)
    print('先序遍历:',node)
    
输出
先序遍历 [17, 5, 2, 11, 35, 29]
树的最小结点值 2
树的最大结点值 35
查找节点操作
2
2
查找节点前驱和后继结点操作
2
17
删除节点操作
先序遍历 [17, 5, 2, 11, 35, 29]