什么是平衡二叉树
wiki:https://en.wikipedia.org/wiki/AVL_tree
1.它是一颗空树,即是没有节点的树,或者是具有以下性质的二叉树:
2.树的左子树和右子树的高度之差(平衡因子)的绝对值不能超过1,并且左子树和右子树也是平衡二叉树,如此循环。
其中,平衡因子:左子树的高度 - 右子树的高度,所以左子树高度比右子树高度小,则平衡因子就是负数,反之为正数,相等则为0。
如何维护平衡二叉树
由于一般二叉树可能变成最坏的情况,变成一条链表,这样查找节点时时间的复杂度将会变得很大。由于平衡二叉树的特点,可以避免这样最坏的情况,平衡二叉树的查找时间复杂度为O(logn)。
虽然平衡二叉树的查找比较有优势,但是每次插入或者删除节点时,需要维护它平衡二叉树的特点不变,即在插入或者删除节点时,需要有左旋或者右旋的操作来维护平衡的特点。
从树的叶子往树的根逐个节点检查二叉树各个子树的平衡因子。
右旋
当一颗树(这里说到的一颗树可以是对整颗树,也可以是一颗树中其中的一部分的子树)的左子树的高度比右子树的高度大于1时,则需要右旋操作。
例如:
显然4的平衡因子大于1了,为了保持平衡那我们就这样做:让4节点的左孩子指向3的右子树(此时为NULL),让3的右孩子指向4,让树根指向3,如图
这种操作我们规定为右旋操作,此图是以4为根进行旋转。
假设这颗树(左子树lTree, 树根root, 右子树rTree),以树根root为支点,进行右旋。
将root转为lTree的根节点,root也就变成lTree的一部分,也可以这样理解,这样的话那么左子树的高度就增加了1,处理完左子树之后,就需要选出这颗树新的根节点,新的根节点需要从rTree中选出,根节点需要比左子树都大(也即是比左子树的所有节点都小),比右子树都小,则只要从右子树中找出最小的节点作为树新的根节点即可,找出右子树中的最左边的节点作为新的根节点,如果没有最左边的节点,则提取右子树的根节点,这样右子树的高度就减少了1,而之前左子树高度增加了1,整颗树的高度就趋向于平衡了。
注意: 如果根root与它的右孩子的平衡因子的符号不同,例如根为正右孩子为负,或者根为负右孩子为正,则先以在右孩子为支点左旋,然后再以根root右旋。
左旋
当一颗树(这里说到的一颗树可以是对整颗树,也可以是一颗树中其中的一部分的子树)的右子树的高度比左子树的高度大于1时,则需要左旋操作。
例如
显然节点4不平衡了。那我们就把4的右孩子7的左子树(此时为NULL),让7的左孩子指向4,让3的右孩子指向7,如图:
我们规定此操作为左旋操作,此图是以4为根进行旋转。
假设这颗树(左子树lTree, 树根root, 右子树rTree),以树根root为支点,进行左旋。
将root转为rTree的根节点,root也就变成rTree的一部分,也可以这样理解,这样的话那么右子树的高度就增加了1,处理完右子树之后,就需要选出这颗树新的根节点,新的根节点需要从lTree中选出,根节点需要比右子树都大(也即是比右子树的所有节点都小),比左子树都大,则只要从左子树中找出最大的节点作为树新的根节点即可,找出左子树中的最右边的节点作为新的根节点,如果没有最右边的节点,则提取左子树的根节点,这样左子树的高度就减少了1,而之前右子树高度增加了1,整颗树的高度就趋向于平衡了。
现在我们在原来基本上插入10节点:
显然节点9不平衡,且是右边高,那我们左旋吧,左旋后的效果是上图右图所示。显然这是不对的,10比11小,但在11的右孩子上。(根本原因是9和11的平衡因子符号不同)。
由于9和11的平衡因子符号不同,需要先以9的右孩子11为中心进行右旋,
然后再以9为中心左旋。
如果根root与它的左孩子的平衡因子的符号不同,例如根为正左孩子为负,或者根为负左孩子为正,则先以在左孩子为支点右旋,然后再以根root左旋。
参照: http://lib.csdn.net/article/datastructure/9204