如何理解平衡二叉树

1.学习平衡二叉树的时候最难理解的一点就是为什么平衡二叉树能够保证任意一个节点的左子树和右子树的高度差为0,1,-1
2.首先我们发现二叉树是对称的。我们可以通过插入数据1,2,3,4,5,6的方式去发现一般规律,总结怎样插入或者旋转才能使它达到每个节点的左子树和右子树的高度差为0,1,-1.
3.从特殊的案例归纳总结一般的结论

这里我们采用的方法是一种数学归纳法
1.当节点个数为1时,只有一个根节点,左子树和右子树的高度差为0
2.当节点个数为n时,我们假设结论成立,也就是n个节点的任意一个节点的左右子树的高度差为0,-1,1.
3.当插入第n+1个节点时,通过旋转等方式,证明当n+1时结论也是成立的。具体证明方式可以参考别人的博客(画图)。也就是当插入节点的父节点的高度差是0,-1,1的情况下,如何通过一些方式,比如旋转来达到最终每个节点的左右子树的高度差为0,-1,1.
4.当n成立时,n+1就成立。我们按照归纳的方式证明,当n=1是,n=2成立,当n=2成立时,n=3成立,以此类推。这也是数学归纳法的基本原理

下面是我用go实现的平衡二叉树的插入
balancetree.go

package main

import "fmt"

// 二叉平衡树
const (
    Equal      = iota // 左右子树高度平衡
    LeftHeavy  = 1    //左边树的高度大于右边
    RightHeavy = -1   // 右子树的高度大于左边
)

type balanceTree struct {
    value            int
    left             *balanceTree
    right            *balanceTree
    heightDifference int8
    parent           *balanceTree
}

var (
    root *balanceTree
    // currentTree *balanceTree
)

// 插入节点
func insert(value int) {
    if root == nil {
        root = &balanceTree{value: value}
        return
    }
    currentTree := root
    insert1(currentTree, value)
}

func insert1(currentTree *balanceTree, value int) {
    if value < currentTree.value { //向左边查找
        if currentTree.left == nil {
            currentTree.left = &balanceTree{value: value, parent: currentTree}
            fixHeight(currentTree, value)
        } else {
            insert1(currentTree.left, value)
        }
    } else if value > currentTree.value { //向右边查找
        if currentTree.right == nil {
            currentTree.right = &balanceTree{value: value, parent: currentTree}
            fixHeight(currentTree, value)
        } else {
            insert1(currentTree.right, value)
        }
    } else { //以前插入过
        fmt.Printf("value=%d已经存在\n", value)
    }
}

// 修复树的高度
func fixHeight(tree *balanceTree, insertValue int) {
    if tree == nil {
        return
    }
    switch tree.heightDifference {
    case Equal:
        if insertValue < tree.value { //插入的是左边
            tree.heightDifference = LeftHeavy
        } else { //插入的是右边
            tree.heightDifference = RightHeavy
        }
        fixHeight(tree.parent, insertValue)
    case LeftHeavy:
        if insertValue < tree.value {
            rightRotat(tree, insertValue)
        } else { //插入的是右边
            //原来左边高度是n+1,右边是n,现在右边增加了1就变成相等了
            tree.heightDifference = Equal //高度差变为0
        }
    case RightHeavy:
        if insertValue < tree.value { //插入的是左边+
            tree.heightDifference = Equal //高度差变为0
        } else {
            leftRotat(tree, insertValue)
        }

    }
}

// 右旋
func rightRotat(tree *balanceTree, insertValue int) {
    parent := tree.parent
    //右边旋转
    left := tree.left
    // 改变左边的树
    left.heightDifference = Equal //高度差变为0
    // left.left = left.left  //左边没有改变
    leftRight := tree.left.right
    left.right = tree
    left.parent = parent
    //左边的父亲节点,指向当前节点的父亲节点
    if parent != nil {
        if parent.value > left.value {
            parent.left = left
        } else {
            parent.right = left
        }
    } else {
        root = left
    }
    //更改当前树
    tree.left = leftRight
    tree.heightDifference = Equal //高度差变为0
    tree.parent = left
    // tree.right=tree.right

}

// 左旋
func leftRotat(tree *balanceTree, insertValue int) {
    //插入的是右边,需要进行左旋
    right := tree.right
    parent := tree.parent
    right.parent = parent
    right.heightDifference = Equal //高度差变为0
    // right.right=right.right
    rightLeft := tree.right.left
    right.left = tree
    if parent != nil {
        if parent.value > right.value {
            parent.left = right
        } else {
            parent.right = right
        }
    } else {
        root = right
    }
    // tree.left=tree.left
    tree.right = rightLeft
    tree.parent = right
    tree.heightDifference = Equal //高度差变为0
}

main.go

package main

import "fmt"

func main() {

    for i := 1; i <= 20; i++ {
        fmt.Println("")
        insert(i)
        totalLayers := calLays(i)
        leveltraversal(totalLayers)
        fmt.Println("前序遍历")
        preOrder(root)
        fmt.Println("")
        fmt.Println("中序遍历")
        inOrder(root)
        fmt.Println("")
        fmt.Println("后序遍历")
        postOrder(root)
        fmt.Println("")

    }

}

print.go

package main

import "fmt"

// 第一到n层的数量是 1,2,4,8,..
// 本层的数量是上面一层的2倍,第一层数量为1
// 计算层数
// count代表总共有多少个数
func calLays(count int) int {
    if count == 0 {
        return 0
    }
    var total int = 0
    var levelCount = 1
    var level int = 1
    for {
        total += levelCount
        if total >= count {
            return level // 两边子树的层数可能不一致,比如左边比右边高或者右边比左边高
        }
        levelCount = levelCount * 2
        level++
    }
}

// 打印每一层数据
// 没有的节点用0表示
func leveltraversal(totalLayers int) {
    var level int //第几层,从0开始
    var info = make([][]*balanceTree, totalLayers)
    info[level] = append(info[level], root)
    for {
        nextLevel := level + 1
        if nextLevel > totalLayers || len(info[level]) <= 0 {
            break
        }
        for i := 0; i < len(info[level]) && nextLevel < totalLayers; i++ {
            if info[level][i].left != nil {
                info[nextLevel] = append(info[nextLevel], info[level][i].left)
            } else if info[level][i].value != 0 {
                info[nextLevel] = append(info[nextLevel], &balanceTree{value: 0})
            }
            if info[level][i].right != nil {
                info[nextLevel] = append(info[nextLevel], info[level][i].right)
            } else if info[level][i].value != 0 {
                info[nextLevel] = append(info[nextLevel], &balanceTree{value: 0})
            }
        }
        level = nextLevel
    }
    for level := 0; level < len(info); level++ {
        var s string
        for i := 0; i < len(info[level]); i++ {
            s += fmt.Sprintf("%d ", info[level][i].value)
        }
        if s != "" {
            fmt.Println("字符串" + s)
        }
    }
    fmt.Println("")

}

// 前序遍历
// 先根,然后左子树,最后右子树
func preOrder(root *balanceTree) {
    if root == nil {
        return
    }
    fmt.Printf("%d ", root.value)
    preOrder(root.left)
    preOrder(root.right)
}

// 中序遍历
func inOrder(root *balanceTree) {
    if root == nil {
        return
    }
    inOrder(root.left)
    fmt.Printf("%d ", root.value)
    inOrder(root.right)
}

// 后序遍历
func postOrder(root *balanceTree) {
    if root == nil {
        return
    }
    postOrder(root.left)
    postOrder(root.right)
    fmt.Printf("%d ", root.value)
}

以上纯属个人见解,如果有什么错误,敬请留言指正,谢谢

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

推荐阅读更多精彩内容