数据结构(Python实现)------二叉搜索树

数据结构(Python实现)------二叉搜索树

二叉搜索树简介

基本概念

在这里插入图片描述
在这里插入图片描述
这篇文章之后,我们提供了一个习题来让你验证一个树是否是二叉搜索树。 你可以运用我们上述提到的性质来判断。 前一章介绍的递归思想也可能会对你解决这个问题有所帮助。

像普通的二叉树一样,我们可以按照前序、中序和后序来遍历一个二叉搜索树。 但是值得注意的是,对于二叉搜索树,我们可以通过中序遍历得到一个递增的有序序列。因此,中序遍历是二叉搜索树中最常用的遍历方法。

在文章习题中,我们也添加了让你求解二叉搜索树的中序后继节点(in-order successor)的题目。显然,你可以通过中序遍历来找到二叉搜索树的中序后继节点。 你也可以尝试运用二叉搜索树的特性,去寻求更好的解决方案。

Python实现

验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
在这里插入图片描述
方法一:
对于二叉搜索树,我们可以通过中序遍历得到一个递增的有序序列。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def isValidBST(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        def inorderTraversal(root):
            if not root:
                return []
            res = []
            res += inorderTraversal(root.left)
            res.append(root.val)
            res += inorderTraversal(root.right)
            return res
        res = inorderTraversal(root)
        
        if res != sorted(list(set(res))) : 
            return False
        return True
                

方法二:
如果左子树的值小于根的值并且右子树的值大于根的值,并进行递归,成立则为二叉搜索树,否则则不是。

class Solution2(object):
    def isValidBST(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        min = max = None
        return self.helper(root,min,max)
    def helper(self,root,min,max):
        if  root is None:
            return True
        if min is not None and root.val <= min:
            return False
        if max is not None and root.val >= max:
            return False
        if self.helper(root.left,min,root.val) and self.helper(root.right,root.val,max):
            return  True
        else:
            return  False

二叉搜索树迭代器

实现一个二叉搜索树迭代器。你将使用二叉搜索树的根节点初始化迭代器。
在这里插入图片描述

调用 next() 将返回二叉搜索树中的下一个最小的数。

第一种思路:
利用BST的性质:BST的中序遍历为升序数组。
所以直接在init里用中序遍历然后存进数组里就好了。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class BSTIterator(object):
    def __init__(self, root):
        """
        :type root: TreeNode
        """
        self.res = []
        def inorder(node):
            if not node:
                return 
            inorder(node.left)
            self.res.append(node.val)
            inorder(node.right)
            
        inorder(root)
        self.index = 0

    def next(self):
        """
        @return the next smallest number
        :rtype: int
        """
        self.index += 1
        return self.res[self.index - 1]
    
    def hasNext(self):
        """
        @return whether we have a next smallest number
        :rtype: bool
        """
        return  self.index < len(self.res)


        # Your BSTIterator object will be instantiated and called as such:
        # obj = BSTIterator(root)
        # param_1 = obj.next()
        # param_2 = obj.hasNext()

第二种思路:
利用一个stack来存。
pushLeft这个函数的功能是把 一个节点和它的左孩子以及左孩子的左孩子… 压入栈。

class BSTIterator2(object):
 
    def __init__(self, root):
        """
        :type root: TreeNode
        """
        self.stack = []
        self.pushLeft(root)
 
    def next(self):
        """
        @return the next smallest number
        :rtype: int
        """
        popedNode = self.stack.pop()
        self.pushLeft(popedNode.right)
        return popedNode.val
 
    def hasNext(self):
        """
        @return whether we have a next smallest number
        :rtype: bool
        """
        return len(self.stack) != 0
    
    def pushLeft(self, node):
        while(node):
            self.stack.append(node)
            node = node.left

在二叉搜索树中实现搜索操作

基本概念

在二叉搜索树中实现搜索操作

二叉搜索树主要支持三个操作:搜索、插入和删除。 在本章中,我们将讨论如何在二叉搜索树中搜索特定的值。

根据BST的特性,对于每个节点:

如果目标值等于节点的值,则返回节点;
如果目标值小于节点的值,则继续在左子树中搜索;
如果目标值大于节点的值,则继续在右子树中搜索。

我们一起来看一个例子:我们在上面的二叉搜索树中搜索目标值为 4 的节点。
在这里插入图片描述

在二叉搜索树中实现插入操作 - 介绍

二叉搜索树中的另一个常见操作是插入一个新节点。有许多不同的方法去插入新节点,这篇文章中,我们只讨论一种使整体操作变化最小的经典方法。 它的主要思想是为目标节点找出合适的叶节点位置,然后将该节点作为叶节点插入。 因此,搜索将成为插入的起始。

与搜索操作类似,对于每个节点,我们将:

根据节点值与目标节点值的关系,搜索左子树或右子树;
重复步骤 1 直到到达外部节点;
根据节点的值与目标节点的值的关系,将新节点添加为其左侧或右侧的子节点。
这样,我们就可以添加一个新的节点并依旧维持二叉搜索树的性质。

在二叉搜索树中实现删除操作

删除要比我们前面提到过的两种操作复杂许多。有许多不同的删除节点的方法,这篇文章中,我们只讨论一种使整体操作变化最小的方法。我们的方案是用一个合适的子节点来替换要删除的目标节点。根据其子节点的个数,我们需考虑以下三种情况:

  1. 如果目标节点没有子节点,我们可以直接移除该目标节点。
  2. 如果目标节只有一个子节点,我们可以用其子节点作为替换。
  3. 如果目标节点有两个子节点,我们需要用其中序后继节点或者前驱节点来替换,再删除该目标节点。

我们来看下面这几个例子,以帮助你理解删除操作的中心思想:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Python实现

Search in a Binary Search Tree

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。
在这里插入图片描述

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def searchBST(self, root, val):
        """
        :type root: TreeNode
        :type val: int
        :rtype: TreeNode
        """
        if root:
            def findsubtree(r,value):
                if not r:
                    return None
                elif r.val == value:
                    return r
                else:
                    a = findsubtree(r.left,val)
                    if not a:
                        b=findsubtree(r.right,value)
                        return b
                    return a

            return findsubtree(root,val)
        return []

精致一点的递归:

class Solution(object):
    def searchBST(self,root,val):
        if not root:
            return None
        if root.val == val:
            return root
        elif root.val < val:
            return self.searchBST(root.right,val)
        else:
            return self.searchBST(root.left,val)

Insert into a Binary Search Tree

给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 保证原始二叉搜索树中不存在新值。

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回任意有效的结果。

例如,
在这里插入图片描述

class TreeNode(object):
     def __init__(self, x):
         self.val = x
         self.left = None
         self.right = None

class Solution(object):
    def insertIntoBST(self, root, val):
        """
        :type root: TreeNode
        :type val: int
        :rtype: TreeNode
        """
        if not root:
            node = TreeNode(val)
            return node

        if val < root.val:
            if not root.left:
                node = TreeNode(val)
                root.left = node

            else:
                self.insertIntoBST(root.left,val)

        else:
            if not root.right:
                node = TreeNode(val)
                root.right = node
            else:
                self.insertIntoBST(root.right,val)

        return root

精简一些的递归

class Solution2(object):
    def insertIntoBST(self, root, val):
        if not root:
            return TreeNode(val)
        if val > root.val:
            root.right = self.insertIntoBST(root.right,val)
        if val < root.val:
            root.left = self.insertIntoBST(root.left,val)

        return root

Delete Node in a BST

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。

示例:
在这里插入图片描述

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def deleteNode(self, root, key):
        """
        :type root: TreeNode
        :type key: int
        :rtype: TreeNode
        """
        if not root:
            return None
        if root.val==key:
            if not root.right:
                left=root.left
                return left
            else:
                right=root.right
                while right.left:
                    right = right.left

                root.val,right.val = right.val,root.val


        root.left = self.deleteNode(root.left,key)
        root.right = self.deleteNode(root.right,key)

        return root

小结

基本概念

Python实现

Kth Largest Element in a Stream

方法一:直接降序排序,然后取第k个元素返回,add时每次都再排序一次,这样时间复杂度为O(k*logk)

class KthLargest(object):
    def __init__(self, k, nums):
        """
        :type k: int
        :type nums: List[int]
        """
        self.nums = nums
        self.k = k

        self.nums.sort(reverse = True)
        while len(self.nums) >k:
            self.nums.pop()





    def add(self, val):
        """
        :type val: int
        :rtype: int
        """
        self.nums.append(val)
        self.nums.sort(reverse = True)
        if len(self.nums) > self.k:
            self.nums.pop()

        return self.nums[-1]



        # Your KthLargest object will be instantiated and called as such:
        # obj = KthLargest(k, nums)
        # param_1 = obj.add(val)

方法二:使用小顶堆实现的优先队列,Python 中标准库 heapq 就是小顶堆,时间复杂度降低为O(k)

import heapq
class KthLargest2(object):
    def __init__(self,k,nums):
        self.pool = nums
        heapq.heapify(self.pool)
        self.k = k
        while len(self.pool) > k:
            heapq.heappop(self.pool)

    def add(self,val):
        if len(self.pool) < self.k:
            heapq.heappush(self.pool,val)
        elif val > self.pool[0]:
            heapq.heapreplace(self.pool,val)

        return self.pool[0]

二叉搜索树的最近公共祖先

非递归方法:

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        while root:
            if root.val > max(p.val,q.val):
                root = root.left
            elif root.val < min(p.val,q.val):
                root = root.right
            else:
                return root


        return root

递归方法

class Solution2(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        if root.val < p.val and root.val <q.val:
            return self.lowestCommonAncestor(root.right,p,q)
        elif root.val > p.val and root.val > q.val:
            return self.lowestCommonAncestor(root.left,p,q)
        else:
            return root

存在重复元素 III

给定一个整数数组,判断数组中是否有两个不同的索引 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ。

示例 1:

输入: nums = [1,2,3,1], k = 3, t = 0
输出: true

示例 2:

输入: nums = [1,0,1,1], k = 1, t = 2
输出: true

示例 3:

输入: nums = [1,5,9,1,5,9], k = 2, t = 3
输出: false

基本概念

什么是一个高度平衡的二叉搜索树?

树结构中的常见用语:

节点的深度 - 从树的根节点到该节点的边数
节点的高度 - 该节点和叶子之间最长路径上的边数
树的高度 - 其根节点的高度

一个高度平衡的二叉搜索树(平衡二叉搜索树)是在插入和删除任何节点之后,可以自动保持其高度最小。也就是说,有N个节点的平衡二叉搜索树,它的高度是logN。并且,每个节点的两个子树的高度不会相差超过1
在这里插入图片描述
在这里插入图片描述
根据定义, 我们可以判断出一个二叉搜索树是否是高度平衡的 (平衡二叉树)。

正如我们之前提到的, 一个有N个节点的平衡二搜索叉树的高度总是logN。因此,我们可以计算节点总数和树的高度,以确定这个二叉搜索树是否为高度平衡的。

同样,在定义中, 我们提到了高度平衡的二叉树一个特性: 每个节点的两个子树的深度不会相差超过1。我们也可以根据这个性质,递归地验证树。

为什么需要用到高度平衡的二叉搜索树?

我们已经介绍过了二叉树及其相关操作, 包括搜索、插入、删除。 当分析这些操作的时间复杂度时,我们需要注意的是树的高度是十分重要的考量因素。以搜索操作为例,如果二叉搜索树的高度为h,则时间复杂度为O(h)。二叉搜索树的高度的确很重要。

所以,我们来讨论一下树的节点总数N和高度h之间的关系。 对于一个平衡二叉搜索树, 我们已经在前文中提过, 。但对于一个普通的二叉搜索树, 在最坏的情况下, 它可以退化成一个链。

因此,具有N个节点的二叉搜索树的高度在logN到N区间变化。也就是说,搜索操作的时间复杂度可以从logN变化到N。这是一个巨大的性能差异。

所以说,高度平衡的二叉搜索树对提高性能起着重要作用

如何实现一个高度平衡的二叉搜索树?

有许多不同的方法可以实现。尽管这些实现方法的细节有所不同,但他们有相同的目标:

采用的数据结构应该满足二分查找属性和高度平衡属性。
采用的数据结构应该支持二叉搜索树的基本操作,包括在O(logN)时间内的搜索、插入和删除,即使在最坏的情况下也是如此。
我们提供了一个常见的的高度平衡二叉树列表供您参考:

红黑树
AVL树
伸展树
树堆

我们不打算在本文中展开讨论这些数据结构实现的细节。

高度平衡的二叉搜索树的实际应用

高度平衡的二叉搜索树在实际中被广泛使用,因为它可以在O(logN)时间复杂度内执行所有搜索、插入和删除操作。

平衡二叉搜索树的概念经常运用在Set和Map中。 Set和Map的原理相似。 我们将在下文中重点讨论Set这个数据结构。

Set(集合)是另一种数据结构,它可以存储大量key(键)而不需要任何特定的顺序或任何重复的元素。 它应该支持的基本操作是将新元素插入到Set中,并检查元素是否存在于其中。

通常,有两种最广泛使用的集合:散列集合(Hash Set)和树集合(Tree Set)。

树集合, Java中的Treeset或者C++中的set,是由高度平衡的二叉搜索树实现的。因此,搜索、插入和删除的时间复杂度都是O(logN)。

散列集合, Java中的HashSet或者C++中的unordered_set,是由哈希实现的, 但是平衡二叉搜索树也起到了至关重要的作用。当存在具有相同哈希键的元素过多时,将花费O(N)时间复杂度来查找特定元素,其中N是具有相同哈希键的元素的数量。 通常情况下,使用高度平衡的二叉搜索树将把时间复杂度从 O(N) 改善到 O(logN)。

哈希集和树集之间的本质区别在于树集中的键是有序的。

Python实现

平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
在这里插入图片描述
在这里插入图片描述
第一种思路:

对于每个node,都判断一下其左右子树的相对高度,然后根据相对高度判断这个node满不满足条件,

再递归地判断其左右孩子满不满足条件。

这种思路会重复计算高度,所以会慢。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def isBalanced(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        if not root:
            return True
        def getHeight(node,h):
            if not node:
                return h
            h+=1
            return max(getHeight(node.left,h),getHeight(node.right,h))

        l_height = getHeight(root.left,0)
        r_height = getHeight(root.right,0)
        return abs(l_height - r_height) <= 1 and self.isBalanced(root.left) and self.isBalanced(root.right)

第二种思路:
如果每一步都计算树高,效率太低,故转变了思想。 递归判断每一个节点的均衡状态,并且只计算一次树高,直至根节点。

class Solution2(object):
    def isBalanced(self,root):
        if not root:
            return True
        def check(root,height):
            if not root:
                return True,height

            tag1,height1 = check(root.left,height+1)
            tag2,height2 = check(root.right,height+1)
            if tag1 and tag2 and abs(height1-height2)<2:
                return True,max(height1,height2)
            else:
                return False,height1


        tag,height = check(root,0)
        return tag

将有序数组转换为二叉搜索树

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
在这里插入图片描述

# Definition for a binary tree node.
class TreeNode(object):
     def __init__(self, x):
         self.val = x
         self.left = None
         self.right = None

class Solution(object):
    def sortedArrayToBST(self, nums):
        """
        :type nums: List[int]
        :rtype: TreeNode
        """
        if not nums:
            return None
        if len(nums) == 1:
            return TreeNode(nums[0])
        mid = len(nums) //2
        root = TreeNode(nums[mid])
        root.left = self.sortedArrayToBST(nums[:mid])
        root.right = self.sortedArrayToBST(nums[mid+1:])
        return root

发布了64 篇原创文章 · 获赞 9 · 访问量 4342

猜你喜欢

转载自blog.csdn.net/Avery123123/article/details/103790811