AVL平衡二叉树

来源:互联网 发布:睡眠软件app 编辑:程序博客网 时间:2024/06/16 10:33

平衡二叉树定义

AVL树也就是所谓的平衡二叉树,查找、删除、插入最坏情况下为O(log n),维持树的平衡只需要几次旋转即可。保证任意一个节点的左子树高度与右子树高度相差不大于1,默认树为空时高度为0。

节点结构

为了后续操作简洁性,先给出节点结构

final class node{int value=0;//键值int height=1;//节点高度node left=null;//左右孩子node right=null;node(int value){this.value=value;}}

旋转类型

维持平衡的操作就是树的旋转,旋转类型可以分为四种:

1.右旋LL类型




单个右旋,将k1的右子树作为k2的左子树,k2作为k1的右子树。该操作只是对于k1,k2进行,所以X,Y,X子树的高度不变,在旋转完成后,只需更新k1,k2高度即可

node l_l_rotate(node root){//LL旋转node temp=root.left;//将左节点拿出来root.left=temp.right;temp.right=root;root.height=Math.max(get_height(root.left),get_height(root.right))+1;//更新原root的heightroot=temp;root.height=Math.max(get_height(root.left),get_height(root.right))+1;//更新新root的heightreturn root;}

简单说就是,将节点的左节点的右子树作为节点的左子树,节点作为其原来左节点的右子树。

2.左旋RR性




跟右转刚好反过来,k2的左子树作为k1的右子树,k1作为k2的左子树。跟上面一样,更改完成后,更新k1,k2的高度即可

node r_r_rotate(node root){//RR旋转node temp=root.right;//将右节点拿出来root.right=temp.left;temp.left=root;root.height=Math.max(get_height(root.left),get_height(root.right))+1;//更新原root的heightroot=temp;root.height=Math.max(get_height(root.left),get_height(root.right))+1;//更新新root的heightreturn root;}

节点的右节点的左子树作为节点的右子树,节点作为其原来右节点的左子树。

3.先左旋再右旋LR




两次操作而已,k1,k2执行RR旋转,然后对k3执行LL右旋操作。

node l_r_rotate(node root){//LR旋转root.left=r_r_rotate(root.left);//先RR旋转return l_l_rotate(root);//再LL旋转}

4.先右旋再左旋RL




先对k3,k2执行LL右旋,再对k1执行RR左旋操作。

node r_l_rotate(node root){//RL旋转root.right=l_l_rotate(root.right);//先LL旋转return r_r_rotate(root);//再LL旋转}

(用LL表示右旋操作,是因为LL表示的是结构上是个“左边形”,所以要向右旋转)

判断旋转操作

node check(node root){//检查高度,判断是否需要旋转操作root.height=(Math.max(get_height(root.left),get_height(root.right))+1);//更新节点高度if(get_height(root.left)-get_height(root.right)==2){//左边增高了if(get_height(root.left.left)>=get_height(root.left.right)){//LL型root=l_l_rotate(root);//LL旋转}else{//LR型root=l_r_rotate(root);//LR旋转}}else if(get_height(root.right)-get_height(root.left)==2){//右边增高了if(get_height(root.right.left)>get_height(root.right.right)){//RL型root=r_l_rotate(root);//RL旋转}else{//RR型root=r_r_rotate(root);//RR旋转}}return root;}

判断很简单,在插入或者删除操作之后,在从根节点到操作的(插入或删除)的那个节点的这条路径上,更新该路径上的节点的高度,并判断是否需要旋转操作来维持平衡。

插入节点操作

从根节点开始到插入位置的这条路径上,在插入节点操作完成之后之后,需要依次从下(插入节点)向上,更新每个该路径上的节点的高度(height属性)信息,借助上面的check函数,在需要旋转的情况下判断条件,进行旋转操作。

由此可知,插入操作需要以递归函数的方式进行。原因如下:

1.建立新插入的节点与原有节点(父节点)之间的引用关系

例如,待插入节点的value值小于root节点时:root.left=insert(root.left,value);将新建立的节点作为其父节点的左子节点,建立联系。

2.回退时更新父节点的高度(height属性)信息

在完成最底层,带插入位置的插入操作之后,即root.left=new node(value);进行root节点的高度更新

参考代码:

node insert(node root,int value){//插入节点if(root==null){//找到插入位置node t=new node(value);//创建该节点return t;}if(root.value==value){//相等就算了,不过此处没有优化,相等的节点上层仍需要check检查System.out.println("already exists");return root;}if(value<root.value){//左子树上创建节点root.left=insert(root.left,value);}else{//右子树上创建节点root.right=insert(root.right,value);}root=check(root);//检查高度,判断是否需要旋转操作return root;}

删除节点操作

删除节点有两个情况

1.待删除的节点具有左子树和右子树

这种情况下待删除的节点可以看做是个官二代,要把他删除了,但有权有势的人家直接找了个替罪羊,自己改个名字照样活的自在。真实情况应该是,将节点删除,在其左子树和右子树中,选择较高的一个子树的极值,放于删除节点出,无图无真相



要删除节点5,则在节点5左子树和右子树中选择较高的一个,这里一样高就随便选了,比如选择右子树,则选择右子树的最小值,即6,放到节点5的位置上;如果选择左子树,则选择最大值,即3,放到5的位置上。然后删除选择的6或者3节点。所以可以看出来,这操作其实根本就不是删除5,而是删除左子树的最大值或者右子树的最小值,5节点一直存在,不过就是换个value值而已,对象不变。

这样的好处,一个是操作简单,不用创建新对象,直接在原对象上修改即可,另外就是可以在删除操作中维持二叉树的平衡,不需要进行旋转操作(当然更新高度还是要有的)

2.待删除的节点不同时具有左子树和右子树

当然不可能每个节点都是官二代,待删除的节点可能是叶子节点或者只有一个子树不为空的节点,此时就需要放弃节点对象了。

删除操作仍然是以递归的形式进行的,原理相同,从根到待删除的节点,在该影响路径上,由下向上依次递归更新。在从根节点找到官二代节点的情形中,同样如此,不过就是换个方向继续向下而已,所以方式一致。

参考代码:

node delete(node root,int value){//根据值来删除节点if(root==null){//即使没找到也会check检查heightSystem.out.println("not found");return null;}if(value<root.value){//左子树上去找root.left=delete(root.left,value);}else if(value>root.value){//右子树上去找root.right=delete(root.right,value);}else{//找到了if(root.left!=null&&root.right!=null){//左右都不等于null,删除之后仍然平衡if(root.left.height>root.right.height){//只是需要更新heightroot.value=max(root.left);//取左子树最大值root.left=delete(root.left,root.value);//删除该最大值}else{root.value=min(root.right);//取右子树最小值root.right=delete(root.right,root.value);//删除该最小值}}else{//左右子树部分或者全部为nullroot=(root.left!=null?root.left:root.right);return root;}}root=check(root);return root;}

参考示例:

建立平衡二叉树:1,2,3,7,8,9,4,5,6

删除:4,6


插入3节点时,RR旋转



插入8节点时,RR旋转


插入9节点,RR旋转



添加6节点后,RR左旋

删除4,6操作


删除6节点,LL右旋

参考代码:

public class t{public static void main(String[] args){int[] arr=new int[]{1,2,3,7,8,9,4,5,6};tree tr=new tree();for(int i:arr){tr.insert(i);}tr.delete(4);tr.delete(6);tree.pre_show(tr);    }}final class node{int value=0;//键值int height=1;//节点高度node left=null;//左右孩子node right=null;node(int value){this.value=value;}}final class tree{node root=null;tree(){}void insert(int value){if(root==null){root=new node(value);}else{root=insert(root,value);}}node insert(node root,int value){//插入节点if(root==null){//找到插入位置node t=new node(value);//创建该节点return t;}if(root.value==value){//相等就算了,不过此处没有优化,相等的节点上层仍需要check检查System.out.println("already exists");return root;}if(value<root.value){//左子树上创建节点root.left=insert(root.left,value);}else{//右子树上创建节点root.right=insert(root.right,value);}root=check(root);//检查高度,判断是否需要旋转操作return root;}int get_height(node t){//节点高度,空树高度为0return t==null?0:t.height;}node check(node root){//检查高度,判断是否需要旋转操作root.height=(Math.max(get_height(root.left),get_height(root.right))+1);//更新节点高度if(get_height(root.left)-get_height(root.right)==2){//左边增高了if(get_height(root.left.left)>=get_height(root.left.right)){//LL型root=l_l_rotate(root);//LL旋转}else{//LR型root=l_r_rotate(root);//LR旋转}}else if(get_height(root.right)-get_height(root.left)==2){//右边增高了if(get_height(root.right.left)>get_height(root.right.right)){//RL型root=r_l_rotate(root);//RL旋转}else{//RR型root=r_r_rotate(root);//RR旋转}}return root;}void delete(int value){root=delete(root,value);}node delete(node root,int value){//根据值来删除节点if(root==null){//即使没找到也会check检查heightSystem.out.println("not found");return null;}if(value<root.value){//左子树上去找root.left=delete(root.left,value);}else if(value>root.value){//右子树上去找root.right=delete(root.right,value);}else{//找到了if(root.left!=null&&root.right!=null){//左右都不等于null,删除之后仍然平衡if(root.left.height>root.right.height){//只是需要更新heightroot.value=max(root.left);//取左子树最大值root.left=delete(root.left,root.value);//删除该最大值}else{root.value=min(root.right);//取右子树最小值root.right=delete(root.right,root.value);//删除该最小值}}else{//左右子树部分或者全部为nullroot=(root.left!=null?root.left:root.right);return root;}}root=check(root);return root;}int max(node root){if(root==null){System.out.println("search and not found");return -1;}while(root.right!=null){root=root.right;}return root.value;}int min(node root){if(root==null){System.out.println("search and not found");return -1;}while(root.left!=null){root=root.left;}return root.value;}node l_l_rotate(node root){//LL旋转node temp=root.left;//将左节点拿出来root.left=temp.right;temp.right=root;root.height=Math.max(get_height(root.left),get_height(root.right))+1;//更新原root的heightroot=temp;root.height=Math.max(get_height(root.left),get_height(root.right))+1;//更新新root的heightreturn root;}node r_r_rotate(node root){//RR旋转node temp=root.right;//将右节点拿出来root.right=temp.left;temp.left=root;root.height=Math.max(get_height(root.left),get_height(root.right))+1;//更新原root的heightroot=temp;root.height=Math.max(get_height(root.left),get_height(root.right))+1;//更新新root的heightreturn root;}node l_r_rotate(node root){//LR旋转root.left=r_r_rotate(root.left);//先RR旋转return l_l_rotate(root);//再LL旋转}node r_l_rotate(node root){//RL旋转root.right=l_l_rotate(root.right);//先LL旋转return r_r_rotate(root);//再LL旋转}static void pre_show(tree tr){//前序遍历树pre_show(tr.root);}static void pre_show(node root){//前序遍历树if(root!=null){System.out.println("value:"+root.value+",height:"+root.height);pre_show(root.left);pre_show(root.right);}}}

结果:

E:\>java tvalue:7,height:4value:2,height:3value:1,height:1value:5,height:2value:3,height:1value:8,height:2value:9,height:1

总结

平衡二叉树提供复杂度为O(Log n)的查询、修改操作,通过对树的旋转来保持树的平衡性质。



参考:http://www.cnblogs.com/skywang12345/p/3576969.html#top


0 0
原创粉丝点击