AVL二叉树

AVL二叉查找树

AVL二叉查找树是一种特殊的二叉查找树,其规定

每个节点的左子树和右子树的高度差最多是1

AVL调整算法

AVL树插入一个新的节点到某个节点下破坏AVL树的要求时,对于破坏条件的第一个节点a(最靠近底部/深度最深的节点),具有四种情况:

  • 插入a的左儿子节点的左子树
  • 插入a的左儿子节点的右子树
  • 插入a的右儿子节点的左子树
  • 插入a的右儿子节点的右子树

其中,第一种和第四种可以看成一种情况的镜像,均是插入外侧;第二种和第三种可以看成另一种情况的镜像,均是插入内侧。这两种情况分别对应两种不同调整方法——单旋转和双旋转。其核心思想都相同,都是尽量将违规子树的父节点的位置尽量向上提。

单旋转调整

考虑入下左图所示的情况,假设X与Z的深度相同且,整棵树符合AVL条件:

单旋转

若插入一个小于b的值,则X的深度将+1,从a节点来看,左子树的深度就比右子树大2,不符合条件。调整的方法如右图所示,以下是调整的合理性:

  • 查找树条件:X子树存储的数据小于b,Z子树存储的数据大于a,Y子树存储的数据范围时(b,a),且a>b,由此看出转换后的数依然是一颗查找树。
  • AVL条件:X深度比Z深1,但Z的位置要比X低1,因此a节点开始的树满足AVL条件。a树原来的深度为max{X+2,Y+2,Z+1},现在a树的深度是max{X+1,Y+2,Z+2}。由于原树满足AVL条件,则Y的深度不会比原来X的深度深,所以深度分别为X1+2,X2+1,其中X2=X1+1,所以a节点深度不变,不影响上层AVL结构。

由此,只要将b提为树根,a放到b的右子树,再将Y挂到a的左子树就可以完成调整

待调整指针 调整前 调整后
树根指针 a b
b左儿子(不变) X X
b右儿子 Y a
a左儿子 b Y
a右儿子(不变) Z Z

双旋转

考虑下左图情况

双旋转

设左图为一颗AVL树,X,Y的深度比W,Z浅1(X,Y深度相等,W,Z深度相等),假若在X或Y中插入一个节点,在a节点的AVL条件将不同,需要使用双旋转调整,调整成右图的样子,合理性如下:

  • 查找树条件:对于W中的w,有w<b;对于X中的x,有b<x<c;对于Y中的y,有c<y<a;对于Z中的z,有z>a。且有b<c<a。在右侧数中以上均成立
  • AVL条件:c的子树深度为1+max{W,X},右侧深度为1+max{Y,Z},W,X,Y,Z中有三个相同,另一个比其他都要浅1,则a的AVL条件成立。双旋转处理后c树深度不变,因此不影响上层的AVL条件

调整情况如下

待调整指针 调整前 调整后
树根节点 a c
a左儿子 b Y
a右儿子(不变) Z Z
b左儿子(不变) W W
b右儿子 c X
c左儿子 X b
c右儿子 Y a

同时,双旋转可以看成b-c和c-a之间的两次单旋转:

双旋转转单旋转

代码实现

结构体

type tree_data struct {
    data int
}

type tree_node struct {
    num    int
    height int
    data   tree_data
    parent *tree_node
    left   *tree_node
    right  *tree_node
}

构造函数

func New_tree_node(num int, data tree_data, parent *tree_node) *tree_node {
    node := tree_node{}
    node.num = num
    node.data = data
    node.height = 0
    node.left = nil
    node.right = nil
    node.parent = parent
    return &node
}

插入方法

func (t *tree_node) Insert(num int, data tree_data) {
    if num > t.num {
        if t.right != nil {
            t.right.Insert(num, data)
             //调整算法,向右子树插入,只能是右侧深度破坏条件
            if t.GetLenght(t.right) > t.GetLenght(t.left)+1 {
                if num > t.right.num {
                      //当待插入的数大于右侧标签,为插入右节点的右子树,单旋转
                    t.RightSimpleRotate()
                } else {
                      //当待插入的数小于右侧标签,为插入右节点的左子树,双旋转
                    t.RightDoubleRotate()
                }
            }
        } else {
             //新建节点不会导致条件破坏
            t.right = New_tree_node(num, data, t)
        }
    } else if num < t.num {
        if t.left != nil {
            t.left.Insert(num, data)
             //调整算法,向左子树插入,只能是左侧深度破坏条件
            if t.GetLenght(t.left) > t.GetLenght(t.right)+1 {
                if num < t.left.num {
                      //当待插入的数小于左侧标签,为插入左节点的左子树,单旋转
                    t.LeftSimpleRotate()
                } else {
                      //当待插入的数大于左侧标签,为插入左节点的右子树,双旋转
                    t.LeftDoubleRotate()
                }
            }
        } else {
             //新建节点不会导致条件破坏
            t.left = New_tree_node(num, data, t)
        }
    } else {
        t.data = data
    }
    t.compute_height()
}

单旋转方法

func (t *tree_node) LeftSimpleRotate() {
    if t.parent.left == t {
        t.parent.left = t.left
    } else {
        t.parent.right = t.left
    }
    temp := t.left.right
    t.left.right = t
    t.left.parent = t.parent

    t.parent = t.left
    t.left = temp
}

func (t *tree_node) RightSimpleRotate() {
    if t.parent.left == t {
        t.parent.left = t.right
    } else {
        t.parent.right = t.right
    }
    temp := t.right.left
    t.right.left = t
    t.right.parent = t.parent

    t.parent = t.right
    t.right = temp
}

双旋转

func (t *tree_node) LeftDoubleRotate() {
    t.left.RightSimpleRotate()
    t.LeftSimpleRotate()
}

func (t *tree_node) RightDoubleRotate() {
    t.right.LeftSimpleRotate()
    t.RightSimpleRotate()
}

其他方法

计算深度

func (t *tree_node) compute_height() {
    t.height = int(math.Max(float64(t.GetLenght(t.left)), float64(t.GetLenght(t.right)))) + 1
}

获得深度

func (t *tree_node) GetLenght(node *tree_node) int {
    if node == nil {
        return -1
    } else {
        return node.height
    }[图片上传中...(simple.png-2406ad-1514205437481-0)]

}

遍历

func (t *tree_node) Visit(indent string) {
    fmt.Println(indent, t.num, t.GetLenght(t.left), t.GetLenght(t.right))
    if t.left != nil {
        t.left.Visit(indent + "  ")
    }
    if t.right != nil {
        t.right.Visit(indent + "  ")
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容

  • 目录 0.树0.1 一般树的定义0.2 二叉树的定义 1.查找树ADT 2.查找树的实现2.1 二叉查找树2.2 ...
    王侦阅读 7,223评论 0 3
  • 一直以来,我都很少使用也避免使用到树和图,总觉得它们神秘而又复杂,但是树在一些运算和查找中也不可避免的要使用到,那...
    24K男阅读 6,753评论 5 14
  • 基于树实现的数据结构,具有两个核心特征: 逻辑结构:数据元素之间具有层次关系; 数据运算:操作方法具有Log级的平...
    yhthu阅读 4,274评论 1 5
  • 树的概述 树是一种非常常用的数据结构,树与前面介绍的线性表,栈,队列等线性结构不同,树是一种非线性结构 1.树的定...
    Jack921阅读 4,460评论 1 31
  • 1. AVL树 AVL树简单来说是带有平衡条件的二叉查找树.传统来说是其每个节点的左子树和右子树的高度最多差1(注...
    fredal阅读 1,834评论 0 4