7.6.2 (python)AVL LeetCode题目 及 AVL结点插入操作详解

这一节关于 AVL 平衡二叉搜索树的题目,刷了不少题,关于平衡树的题目确实很少,做来做去,就先看下面这两道小儿科的题目吧。

 

110. Balanced Binary Tree

Given a binary tree, determine if it is height-balanced.

For this problem, a height-balanced binary tree is defined as:

a binary tree in which the depth of the two subtrees of every node never differ by more than 1.

题目解析:

简单来说,就是检查每个结点左右子树的深度,之差不大于1. 这里构造了一个检查的函数,递归求树深,该结点的深度为左右子树深度的最大值加1, 另一方面,函数中判断左右子树深度的差值,并返回一个布尔值,表示其是否平衡。为了速度快, 当子树已经为不平衡时,会直接向上递归回去结果。代码如下, 速度击败95%.

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def check(root):
            if not root:
                return 0, True
            d1, f1 = check(root.left)
            if not f1:
                return 0, False
            d2, f2 = check(root.right)
            if not f2:
                return 0, False
            if abs(d1-d2) < 2 and f1 and f2:
                return max(d1, d2) + 1, True
            else:
                return 0, False

        d1, f1 = check(root)
        return f1        

 

 108. Convert Sorted Array to Binary Search Tree

Given an array where elements are sorted in ascending order, convert it to a height balanced BST.

For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.

题目解析:

BST中序遍历 就是一个递增的序列, 现在要求你逆操作,构造一棵平衡的二叉搜索树。如下代码借鉴了二分查找,不断二分,mid指向元素作为根结点,两侧依次是其左右子树,当左右子树只有一个元素时停止。代码结合了递归思想,二分思想,构造了一个中间函数,速度极佳, 打败了99%。

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:        
        head = None
        if not nums:
            return head
        def helper(lt, rt):
            if lt == rt:
                return TreeNode(nums[lt])
            mid = (lt + rt) // 2
            p = TreeNode(nums[mid])
            if mid > lt:
                p.left = helper(lt, mid-1)
            if mid < rt:
                p.right = helper(mid+1, rt)            
            return p
        
        head = helper(0, len(nums)-1)      
        return head

上面两道题都是BST相关基本应用+平衡树的概念,但是熟练应用二叉树的常用解题思路,如遍历,递归, 栈,队列等,还需多思考。

前方高能。。。


LeetCode中并没有对AVL的增删操作的考察(反正我没看到),这应该是AVL的基本功,与任何场景都无关,下面我们就认真撕一下这个问题:实现在AVL平衡二叉树中结点的插入。

大家也可参考相关博客,本人借鉴了以下博客内容。

AVL树Python实现(使用递推实现添加与删除)

AVL树的python实现

AVL树的python实现

首先这是树的结构定义:

class AVLTreeNode(object):
    def __init__(self, key):
        self.key = key
        self.p = None
        self.left = None
        self.right = None
        self.height = 0

    def __str__(self):  # 有助于我们后面查看
        return "{ %d : left %s ; right %s }" % (self.key, self.left, self.right)

下面我们看一下核心的旋转代码,先看右旋: 

def right_rotate(root, node):  # 右旋, 用于LL型失衡
    # 先将 node 和 node_left 之间及其左右节点赋值 (node_left.left node.right 保持不变)
    node_left = node.left
    node.left = node_left.right
    node_left.right = node
    # 然后几个相关结点的父子关系梳理
    if not node.p:
        root = node_left
        node_left.p = None
    elif node == node.p.left:
        node.p.left = node_left
        node_left.p = node.p
    elif node == node.p.right:
        node.p.right = node_left
        node_left.p = node.p
    node.p = node_left
    # 调整树高, 由node向上层更新树高
    while node:
        node.height = max(get_height(node.left), get_height(node.right)) + 1
        node = node.p
    return root

配合之前的动图来看:

 再看左旋,

def left_rotate(root, node):  # 适用于RR型失衡
    # 还是一样的三个步骤
    node_right = node.right
    node.right = node_right.left
    node_right.left = node
    if not node.p:
        root = node_right
        node_right.p = None
    elif node == node.p.left:
        node.p.left = node_right
        node_right.p = node.p
    elif node == node.p.right:
        node.p.right = node_right
        node_right.p = node.p
    node.p = node_right
    while node:
        node.height = max(get_height(node.left), get_height(node.right)) + 1
        node = node.p
    return root

因此,LR型失衡先左旋再右旋,RL型失衡先右旋再左旋,代码如下:

def left_right_rotate(root, node):  # LR型失衡
    # 先对左子树左旋, 转为LL型失衡
    root = left_rotate(root, node.left)
    root = right_rotate(root, node)
    return root


def right_left_rotate(root, node):  # RL型失衡
    # 先对右子树右旋,转为RR型失衡
    root = right_rotate(root, node.right)
    root = left_rotate(root, node)
    return root

上面几个函数实现了AVL中失衡类型确定后的调整,下面我们看一下插入结点的全流程,需要注意的是,调整失衡时,当根结点失衡,要特殊对待,此时树的根结点会调整,返回新的根结点。下面代码参考其他博客,并调整了一定的可读性。

def get_height(node):
    return node.height if node else -1


def insert(root, val):  # 入参:树根及插入值, 返回:新的树根(因为可能会变)
    node = AVLTreeNode(val)
    # 空树 把 root 赋值即可, 无需其他步骤
    if not root:
        return node
    temp = root
    temp_node = None
    # 找到要插入的父节点(temp_node) 若值已存在,则抛出错误
    while temp:
        temp_node = temp
        if node.key < temp.key:
            temp = temp.left
        elif node.key > temp.key:
            temp = temp.right
        else:
            raise KeyError("Error!")
    # 父子相认
    if node.key < temp_node.key:
        temp_node.left = node
    elif node.key > temp_node.key:
        temp_node.right = node
    node.p = temp_node
    # 从父结点 temp_node 开始向上更新每个结点的高度
    temp_p = temp_node
    while temp_p:
        temp_p.height = max(get_height(temp_p.left), get_height(temp_p.right)) + 1
        temp_p = temp_p.p
    # 最后从插入结点 node本身开始,调整平衡
    root = balance(root, node)
    return root


def balance(root, node):
    while node:
        # 调整思路很简单,从node向上层,找到第一个失衡的结点,判断失衡类型,进行相应调整
        if get_height(node.left) - get_height(node.right) == 2:
            if node.left.left:
                root = right_rotate(root, node)
            else:
                root = left_right_rotate(root, node)
            return root
        elif get_height(node.right) - get_height(node.left) == 2:
            if node.right.right:
                root = left_rotate(root, node)
            else:
                root = right_left_rotate(root, node)
            return root
        node = node.p
    return root

至于结点的删除,懒得再撕了,需要的话自行掰吃。 

在完成上述代码后,我们如何检验一下呢?把上面所有函数和类都汇总,运行下面main方法。构造的合适的插入顺序,插入过程会遇到四次失衡。

if __name__ == '__main__':
    number_list = (7, 4, 1,  # LL
                   8, 5, 12,  # RR
                   9,        # RL
                   3, 2      # RL
                   )
    root = None
    for number in number_list:
        root = insert(root, number)
        print(root)

我们来看一下最终结果, 自己手动插入以上结点,看看代码运行是否正确,以及自己理解的是否有偏差。

"""
{ 7 : left None ; right None }
{ 7 : left { 4 : left None ; right None } ; right None }
{ 4 : left { 1 : left None ; right None } ; right { 7 : left None ; right None } }
{ 4 : left { 1 : left None ; right None } ; right { 7 : left None ; right { 8 : left None ; right None } } }
{ 4 : left { 1 : left None ; right None } ; right { 7 : left { 5 : left None ; right None } ; right { 8 : left None ; right None } } }
{ 7 : left { 4 : left { 1 : left None ; right None } ; right { 5 : left None ; right None } } ; right { 8 : left None ; right { 12 : left None ; right None } } }
{ 7 : left { 4 : left { 1 : left None ; right None } ; right { 5 : left None ; right None } } ; right { 9 : left { 8 : left None ; right None } ; right { 12 : left None ; right None } } }
{ 7 : left { 4 : left { 1 : left None ; right { 3 : left None ; right None } } ; right { 5 : left None ; right None } } ; right { 9 : left { 8 : left None ; right None } ; right { 12 : left None ; right None } } }
{ 7 : left { 4 : left { 2 : left { 1 : left None ; right None } ; right { 3 : left None ; right None } } ; right { 5 : left None ; right None } } ; right { 9 : left { 8 : left None ; right None } ; right { 12 : left None ; right None } } }
"""

好,这一节艰难的客服过去了。。。 

 

 

操作ES 7.6.2版本,我们可以使用Java编写代码来与ES进行交互。首先,我们需要导入ES的Java客户端库。 在代码中,我们可以创建一个ES的客户端对象,来建立与ES的连接。通过配置ES的主机名和端口,我们可以使用TransportClient或者HighLevelRestClient来创建客户端对象。 一旦与ES建立了连接,我们可以使用客户端对象来执行各种操作,例如创建索引、插入数据、更新数据、删除数据等。 要创建索引,我们可以通过指定索引名、类型和映射来定义一个索引。然后,我们可以通过客户端对象的`indices`方法来执行该操作。 要插入数据,我们可以定义一个JSON对象,然后使用客户端对象的`prepareIndex`方法指定索引、类型和ID来插入数据。 要更新数据,我们可以使用客户端对象的`prepareUpdate`方法指定索引、类型和ID,并提供更新的内容。 要删除数据,我们可以使用客户端对象的`prepareDelete`方法指定索引、类型和ID来删除数据。 除了上述的基本操作外,ES还提供了非常强大的查询功能。我们可以使用客户端对象的`prepareSearch`方法来构建查询条件,并执行查询操作。 最后,当我们完成所有操作后,需要关闭客户端对象,以释放资源。 使用Java操作ES 7.6.2版本可以让我们更灵活地与ES进行交互,实现数据的增删改查等功能。同时,ES的丰富的查询功能也提供了更多的灵活性和强大的扩展性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值