二叉树相关

二叉平衡树(AVL树)基本定义

  1. AVL树里任一结点的左右子树高度(深度)差的绝对值小于等于1,绝对值就是平衡因子
  2. 任一结点的左右子树均为AVL树

AVL树是一种特殊的二叉搜索树 (BST树),数据极端情况下, 二叉搜索树会退化成为单链表,但是AVL树通过旋转操作规避了这个问题。
查找平均复杂度:O(logn)
下面是基本节点定义

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

利用递归计算二叉平衡树的深度(高度)

注:下面的代码都是属于类AVLTree里的方法

def depth(self, node):
	if node is None:
	    return 0
	d1 = self.depth(node.left) # 计算node节点左子树的深度
	d2 = self.depth(node.right) # 计算node节点右子树的深度
	return max(d1, d2) + 1

二叉平衡树之插入节点

首先,二叉平衡树一棵二叉搜索树,其添加节点的方式同二叉搜索树一致。但按照这种方式添加(插入)节点会导致树中的某节点的平衡因子大于1,所以为了使得此树满足二叉平衡树的定义,在插入节点后需要对树中的不平衡点进行调整。根据不同的插入位置,一共有以下四种调整方法。

插入位置调整方式
插入结点在不平衡点右子树的右边LL调整,左旋转
插入结点在不平衡点左子树的左边RR调整,右旋转
插入结点在不平衡点左子树的右边LR调整,左右旋转
插入结点在不平衡点右子树的左边RL调整,右左旋转

注:不平衡点是第一个不满足这棵树是平衡二叉树的结点(平衡因子大于1),如果有多个结点同时不满足,就选最靠近下面的那个结点作为不平衡点。

RR调整

在这里插入图片描述
插入节点是2,上图的10是不平衡点,其左子树高度为2,右子树高度为0,平衡因子为2。需要进行右旋转,RR调整的代码如下:

 def right_rotate(self, root): # root是不平衡点
     if root is None:
          return
      # 创建新的结点,以当前根结点的值
      new_root = TreeNode(root.val)
      # 把新结点的右子树设为当前结点的右子树
      new_root.right = root.right
      # 把新结点的左子树设为当前结点的左子树的右子树
      new_root.left = root.left.right
      # 把当前结点的值替换成它的左子结点的值
      root.val = root.left.val
      # 把当前结点的左子树设置成当前结点的左子树的左子树
      root.left = root.left.left
      # 把当前结点的右子结点设置成(指向)新的结点
      root.right = new_root

RR调整后,整棵树如下图所示,满足二叉平衡树的定义
在这里插入图片描述

LL调整

在这里插入图片描述
插入节点是9,上图的7是不平衡点,其左子树高度为0,右子树高度为2,平衡因子为2。需要进行左旋转,LL调整的代码如下:

def left_rotate(self, root): # root是不平衡点
    if root is None:
        return
    # 创建新的结点,以当前根结点的值
    new_root = TreeNode(root.val)
    # 把新结点的左子树设为当前结点的左子树
    new_root.left =root.left
    # 把新结点的右子树设为当前结点的右子树的左子树
    new_root.right = root.right.left
    # 把当前结点的值替换成它的右子结点的值
    root.val = root.right.val
    # 把当前结点的右子树设置成当前结点的右子树的右子树
    root.right = root.right.right
    # 把当前结点的左子结点设置成(指向)新的结点
    root.left = new_root

LL调整后,整棵树如下图所示,满足二叉平衡树的定义
在这里插入图片描述

LR调整

在这里插入图片描述
插入节点是6,上图的7是不平衡点,其左子树高度为2,右子树高度为0,平衡因子为2。需要进行左右旋转(先对5节点进行左旋转变成RR调整的形式,再对7节点进行右旋转),LR调整的代码如下:

def left_right_rotate(self, root):
    self.left_rotate(root.left)
    self.right_rotate(root)

先进行左旋转,整棵树如下图所示,化成RR调整的形式
在这里插入图片描述
再进行右旋转,整棵树如下图所示,满足平衡二叉树的定义。
在这里插入图片描述

RL调整

在这里插入图片描述
插入节点是8,上图的7是不平衡点,其左子树高度为0,右子树高度为2,平衡因子为2。需要进行右左旋转(先对9节点进行右旋转变成LL调整的形式,再对7节点进行左旋转),RL调整的代码如下:

def right_left_rotate(self, root):
      self.right_rotate(root.right)
      self.left_rotate(root)

先进行右旋转,整棵树如下图所示,化成LL调整的形式
在这里插入图片描述
再进行左旋转,整棵树如下图所示,满足平衡二叉树的定义。
在这里插入图片描述

二叉平衡树之删除节点

二叉平衡树之寻找前继节点和后继节点

本文在不含父亲节点的数据结构下,分析给出了时间复杂度为O(lgN)的求前驱后继结点的算法。
前驱节点
若一个节点有左子树,那么该节点的前驱节点是其左子树中val值最大的节点.
若一个节点没有左子树,那么判断该节点和其父节点的关系.

  1. 若该节点是其父节点的右边孩子,那么该节点的前驱结点即为其父节点。
  2. 若该节点是其父节点的左边孩子,那么需要沿着其父亲节点一直向树的顶端寻找,直到找到一个节点P,P节点是其父节点Q的右边孩子(可参考例子2的前驱结点是1),那么Q就是该节点的后继节点

类似,我么可以得到求后继节点的规则。

后继节点
若一个节点有右子树,那么该节点的后继节点是其右子树中val值最小的节点(也就是右子树中所谓的leftMostNode)
若一个节点没有右子树,那么判断该节点和其父节点的关系
2.1 若该节点是其父节点的左边孩子,那么该节点的后继结点即为其父节点
2.2 若该节点是其父节点的右边孩子,那么需要沿着其父亲节点一直向树的顶端寻找,直到找到一个节点P,P节点是其父节点Q的左边孩子,那么Q就是该节点的后继节点

规则中我们是从下往上找,但实际代码中是不允许我们这么操作的(由于我们没有父亲指针),我们可以在寻找对应val节点的过程中从上向下找,并且过程中记录下parent节点和firstRParent节点(最后一次在查找路径中出现右拐的节点)。
python代码实现如下:

def getPreNode_val(self, val): # 默认该二叉平衡树非空,有根节点
    flag = True # 默认 值为val的节点没有左子树
    root = self.root
    Parent = None
    firstRParent = None
    while root:
        if val == root.val:
            node = root # 值为val的节点
            if root.left:
                flag = False
            break
        Parent = root
        if val < root.val:
            root = root.left
        elif val > root.val:
            firstRParent = root # 出现右拐点
            root = root.right
    if root is None: # 表明上述循环没有找到节点val
        print("值为%d的节点不存在前继节点"%val)
        return
    if Parent is None: # 表示上述循环内的Parent=root语句一次也未执行,此时查询的点即根节点
        node = root.left
        if node is None:
            return root.val
        else: 
            while node.right: # 只要一直有右孩子,就一直循环
                node = node.right
            return node.val
    else: # 查询的节点不是根节点
        if flag: # node节点没有左子树
            if val > Parent.val: # node节点是父亲节点的右孩子,此时Parent等于firstParent,firstParent没有意义
                return Parent.val
            elif val < Parent.val: # node节点是父亲节点的左孩子,此时Parent不等于firstParent,firstParent有意义
                if firstRParent is None:
                    return None # 没有前继节点
                else:
                    return firstRParent.val
        else: # node节点有左子树
            node = node.left
            while node.right: # 只要一直有右孩子,就一直循环
                node = node.right
            return node.val # 对于值为val的节点node,返回其左子树中val值最大的节点

求后继节点
同样,求后继节点我们不能从底向上找,也是从上向下找,首先是找到对应val值的节点,顺便把其的parent节点和firstlParent节点(最后一次在查找路径中出现左拐的节点)。
python代码实现如下:

def getNextNode_val(self, val):
    flag = True # 默认 值为val的节点没有右子树
    root = self.root
    Parent = None
    firstLParent = None
    while root:
        if val == root.val:
            node = root # 值为val的节点
            if root.right: # 有右孩子
                flag = False
            break
        Parent = root
        if val < root.val:
            firstLParent = root # 出现左拐点
            root = root.left
        elif val > root.val:
            root = root.right
    if root is None:
        print("值为%d的节点不存在后继节点"%val)
        return
    if Parent is None: # 表示上述循环内的Parent=root语句一次也未执行,此时查询的点即根节点
        node = root.right
        if node is None:
            return root.val
        else: 
            while node.left: # 只要一直有左孩子,就一直循环
                node = node.left
            return node.val
    else: # 查询的节点不是根节点
        if flag: # node节点没有右子树
            if val < Parent.val: # node节点是父亲节点的左孩子,此时Parent等于firstParent,firstParent没有意义
                return Parent.val
            elif val > Parent.val: # node节点是父亲节点的右孩子,此时Parent不等于firstParent,firstParent有意义
                if firstLParent is None:
                    return None
                else:
                    return firstLParent.val
        else: # node节点有右子树
            node = node.right
            while node.left: # 只要一直有左孩子,就一直循环
                node = node.left
            return node.val # 对于值为val的节点node,返回其左子树中val值最大的节点

判断是否该二叉树是否平衡

def isblanced(self, root):
    def height(node):
        if not node:return 0
        left = height(node.left)
        right = height(node.right)
        if left == -1 or right == -1 or abs(left-right) > 1:
            return -1

        return max(left,right) + 1
    return height(root) != -1

二叉平衡树之先序遍历、中序遍历

# 中序遍历测试
def in_order(self, node):
    if node is None:
        return
    self.in_order(node.left)
    print(node.val, end=" ")
    self.in_order(node.right)

# 前序遍历测试(主要看根结点的变化)
def pre_order(self, node):
    if node is None:
        return
    print(node.val, end=" ")
    self.pre_order(node.left)
    self.pre_order(node.right)

二叉平衡树全部代码

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


class AVLTree(object):
    def __init__(self):
        self.root = None
        self.P = 0
    
    
    def getpower(self):
        return self.P
    
    
    def depth(self, node):
        if node is None:
            return 0
        d1 = self.depth(node.left)
        d2 = self.depth(node.right)
        return max(d1, d2) + 1
        
        
    def left_rotate(self, root):
        if root is None:
            return
        # 创建新的结点,以当前根结点的值
        new_root = TreeNode(root.val)
        # 把新结点的左子树设为当前结点的左子树
        new_root.left =root.left
        # 把新结点的右子树设为当前结点的右子树的左子树
        new_root.right = root.right.left
        # 把当前结点的值替换成它的右子结点的值
        root.val = root.right.val
        # 把当前结点的右子树设置成当前结点的右子树的右子树
        root.right = root.right.right
        # 把当前结点的左子结点设置成(指向)新的结点
        root.left = new_root
    def right_rotate(self, root):
        if root is None:
            return
        # 创建新的结点,以当前根结点的值
        new_root = TreeNode(root.val)
        # 把新结点的右子树设为当前结点的右子树
        new_root.right = root.right
        # 把新结点的左子树设为当前结点的左子树的右子树
        new_root.left = root.left.right
        # 把当前结点的值替换成它的左子结点的值
        root.val = root.left.val
        # 把当前结点的左子树设置成当前结点的左子树的左子树
        root.left = root.left.left
        # 把当前结点的右子结点设置成(指向)新的结点
        root.right = new_root
    def left_right_rotate(self, root):
        self.left_rotate(root.left)
        self.right_rotate(root)
    def right_left_rotate(self, root):
        self.right_rotate(root.right)
        self.left_rotate(root)

    """# 非递归添加结点
    def insert(self, node):
        if self.root is None:
            self.root = node
            return
        queue = [self.root]
        while queue:
            temp_node = queue.pop(0)
            # 判断传入结点的值和当前子树结点的值关系
            if node.val < temp_node.val:
                if temp_node.left is None:
                    temp_node.left = node
                    return
                else:
                    queue.append(temp_node.left)
            if node.val >= temp_node.val:
                if temp_node.right is None:
                    temp_node.right = node
                    return
                else:
                    queue.append(temp_node.right)
    """
    
    # 递归添加结点
    def insert(self, root, val):
        node = TreeNode(val)
        if root is None:
            self.root = node
            return
        if val < root.val:
            if not root.left: # 根的左子树是空的
                root.left = node
                return
            else:
                self.insert(root.left, val)
                if self.depth(root.left) - self.depth(root.right) >= 2:
                    if val < root.left.val:
                        self.right_rotate(root)
                    else:
                        self.left_right_rotate(root)
        else:
            if not root.right: # 根的右子树是空的
                root.right = node
                return
            else:
                self.insert(root.right, val)
                if self.depth(root.right) - self.depth(root.left) >= 2: # 这里的root就是指“递归添加结点那里传入的root”
                    if val > root.right.val:
                        self.left_rotate(root)
                    else:
                        self.right_left_rotate(root)
                        
    def remove(self, root, val):
        

    def isblanced(self, root):
        def height(node):
            if not node:return 0
            left = height(node.left)
            right = height(node.right)
            if left == -1 or right == -1 or abs(left-right) > 1:
                return -1

            return max(left,right) + 1
        return height(root) != -1
    
    def getPreNode_val(self, val): # 默认该二叉平衡树非空,有根节点
        flag = True # 默认 值为val的节点没有左子树
        root = self.root
        Parent = None
        firstRParent = None
        while root:
            if val == root.val:
                node = root # 值为val的节点
                if root.left:
                    flag = False
                break
            Parent = root
            if val < root.val:
                root = root.left
            elif val > root.val:
                firstRParent = root # 出现右拐点
                root = root.right
        if root is None: # 表明上述循环没有找到节点val
            print("值为%d的节点不存在前继节点"%val)
            return
        if Parent is None: # 表示上述循环内的Parent=root语句一次也未执行,此时查询的点即根节点
            node = root.left
            if node is None:
                return root.val
            else: 
                while node.right: # 只要一直有右孩子,就一直循环
                    node = node.right
                return node.val
        else: # 查询的节点不是根节点
            if flag: # node节点没有左子树
                if val > Parent.val: # node节点是父亲节点的右孩子,此时Parent等于firstParent,firstParent没有意义
                    return Parent.val
                elif val < Parent.val: # node节点是父亲节点的左孩子,此时Parent不等于firstParent,firstParent有意义
                    if firstRParent is None:
                        return None # 没有前继节点
                    else:
                        return firstRParent.val
            else: # node节点有左子树
                node = node.left
                while node.right: # 只要一直有右孩子,就一直循环
                    node = node.right
                return node.val # 对于值为val的节点node,返回其左子树中val值最大的节点
        
    def getNextNode_val(self, val):
        flag = True # 默认 值为val的节点没有右子树
        root = self.root
        Parent = None
        firstLParent = None
        while root:
            if val == root.val:
                node = root # 值为val的节点
                if root.right: # 有右孩子
                    flag = False
                break
            Parent = root
            if val < root.val:
                firstLParent = root # 出现左拐点
                root = root.left
            elif val > root.val:
                root = root.right
        if root is None:
            print("值为%d的节点不存在后继节点"%val)
            return
        if Parent is None: # 表示上述循环内的Parent=root语句一次也未执行,此时查询的点即根节点
            node = root.right
            if node is None:
                return root.val
            else: 
                while node.left: # 只要一直有左孩子,就一直循环
                    node = node.left
                return node.val
        else: # 查询的节点不是根节点
            if flag: # node节点没有右子树
                if val < Parent.val: # node节点是父亲节点的左孩子,此时Parent等于firstParent,firstParent没有意义
                    return Parent.val
                elif val > Parent.val: # node节点是父亲节点的右孩子,此时Parent不等于firstParent,firstParent有意义
                    if firstLParent is None:
                        return None
                    else:
                        return firstLParent.val
            else: # node节点有右子树
                node = node.right
                while node.left: # 只要一直有左孩子,就一直循环
                    node = node.left
                return node.val # 对于值为val的节点node,返回其左子树中val值最大的节点
                
                

    # 中序遍历测试
    def in_order(self, node):
        if node is None:
            return
        self.in_order(node.left)
        print(node.val, end=" ")
        self.in_order(node.right)

    # 前序遍历测试(主要看根结点的变化)
    def pre_order(self, node):
        if node is None:
            return
        print(node.val, end=" ")
        self.pre_order(node.left)
        self.pre_order(node.right)


if __name__ == '__main__':
    a = AVLTree()
    # 测试左旋转结点值数组
    # node_array = [4, 3, 6, 5, 7, 8]
    # 测试右旋转结点值数组
    # node_array = [10, 12, 8, 9, 7, 6]
    # 测试双旋转结点数组
    node_array = [10, 11, 7, 6, 8, 9]
    for item in node_array:
        a.insert(a.root, item)
#         a.insert(item)
#     a.judge_node(a.root)
    print("中序遍历结果为:")
    a.in_order(a.root)
    print()
    print("旋转后,前序遍历结果为:")
    a.pre_order(a.root)
    print()
    print("树的高度为:", a.depth(a.root))
    print("左子树高度为:", a.depth(a.root.left))
    print("右子树高度为:", a.depth(a.root.right))

如果本文章对你有帮助的话,麻烦点个赞哦,谢谢!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值