平衡二叉树学习小节
来源:互联网 发布:rf 910编程器驱动程序 编辑:程序博客网 时间:2024/06/06 00:50
平衡二叉树 (Balanced Binary Tree,Height-Balanced Tree,AVL)
这些天大致学习了下平衡二叉树,下面我来分享下我的一些学习体会,新司机上路还望老司机们多多批评指导(:
平衡二叉树在查找中的优势和劣势
AVL树上任何节点的左子树和右子树的高度之差不会超过1,所以它的高度和logn是同数量级的,因此它的平均查找长度也和logn是同数量级的,在大规模数据的查找中相比于其他普通查找算法优势明显。但在数据的插入过程中由于需要进行树的旋转等问题,数据的插入比较麻烦
话不多说开始进入严肃的学习环节
定义:
它或者是一颗空树,或者是具有下列性质的二叉树
1. 它的左子树和右子树都是平衡二叉树
2. 左子树和右子树的高度之差的绝对值不超过1
我们根据数据的处理过程来讲述,我把它分为以下三步:准备工作->插入->查找->删除(先讲述思路最后会附上我自己的实现代码)
1. 准备工作
我所说的准备工作就是先构建一个数据结构,作为数据的载体
这个载体要满足以下几个条件
1. 一个存储数据的类(为学习方便假设为整型int)
2. 两个指针,分别指向左子树和右子树
3. 要能标明节点所在的高度
4. 其它…(根据自己的需求添加)
class TreeNode{int data; int height; TreeNode lson; TreeNode rson;}
2. 插入
2.1 插入节点
插入数据节点的过程和普通二叉树的插入没有区别,只需使父节点的左右子树指针中的某个指向这个节点即可。
现规定:
数据节点的数值>父节点的数值,将父节点的rson指向这个数据节点
数据节点的数值<父节点的数值,将父节点的lson指向这个数据节点
数据节点的数值=父节点的数值,不作处理
插入节点后父节点的高度有可能改变必须要有判断和修改的函数
2.2 树的旋转
但在数据插入之后可能会出现某个节点的左右两个子节点的高度差大于1,此时需要进行旋转操作。具体的旋转方式有以下四种
2.2.1 左旋(L)
当出现某节点的所在子树出现上图所示情况(或是未出现Cr出现对的是Cl)时需要进行左旋来平衡左右子树的高度差
2.2.2 右旋(R)
当出现某节点的所在子树出现上图所示情况(或是未出现Cl出现对的是Cr)时需要进行右旋来平衡左右子树的高度差
2.2.3 先左旋再右旋(LR)
上图所示情况是不可能出现在平衡二叉树中的,原因在于当Cl或Cr出现其中一个时即会出现左右子树高度差大于1的情况,此处只为说明
此时情况与前两种有所不同,若直接进行右旋操作则会变为图4所示情况。
因此可以现对左子树进行一次左旋操作,左旋过后变为图2所示这种情况,因此再进行一次右旋即可实现二叉树的平衡
2.2.4 先右旋再左旋(RL)
原因与2.2.3相同
旋转函数编写的一些思考:
* 旋转前必须要能准确判断此时子树属于哪种情况
* 旋转时根节点可能会参与旋转,所以函数一定要能返回旋转过后的根节点
* 旋转过后树的高度可能会发生变化,因此旋转函数必须能改变参与旋转的节点的高度。(同时旋转子树的父节点的高度也有可能发生变化,但可以在其它函数中更改)
3. 查找
平衡二叉树的查找操作与普通二叉树没有区别。由于其优越的平衡性能使平衡二叉树的高度与logn在同一个数量级,在大量数据处理时表现优异。
4. 删除
我个人认为节点的删除是对个人对平衡二叉树理解的一次检验。
我们首先对删除函数所需要拥有的各种特性进行分析
* 删除的可能是根节点,因此函数必须能返回删除节点后的子树的根节点
* 删除后可能会导致父节点的左右子树高度差大于1的情况,因此一定会用到旋转函数
* 要考虑到删除某个节点后谁来替代这个节点的问题(我用的是子节点替代的方法,即如果没有子节点则将其置空,若只有一个子节点则取左子树的最大节点或是右子树的最小节点取代,若有两个子节点则取高度较高的)
经过我的观察一共有三种情况:
当要删除的节点没有任何子节点时只需将其置空即可
当要删除的节点的左子树高度大于等于右子树的高度时,取左子树中的最大值来代替这个节点
当要删除的节点的右子树高度大于左子树的高度时,取右子树中的最小值来代替这个节点
* 删除节点后可能会引起高度差的变化,因此可能需要改变高度
这些操作分析完毕
5. 下面是我的一些实现代码
import java.util.ArrayList;
import java.util.Scanner;
class TreeNode
{
int data;//值
int height;//以此节点为根的树的高度
TreeNode lson;//指向左儿子的地址
TreeNode rson;//指向右儿子的地址
};
public class BalancedBinaryTree {
public TreeNode root=null;public int queryHeight(TreeNode node){ int h; if(node==null) h=0; else if(node.lson==null&&node.rson==null){ h=1; }else{ if(node.lson==null) h=node.rson.height+1; else if(node.rson==null) h=node.lson.height+1; else h=node.lson.height>node.rson.height?node.lson.height+1:node.rson.height+1; } return h;}/** * 单右旋,当左子树的高度-右子树的高度>1时调用 * @param k2 根节点 * @return 变换过后的根节点 */public TreeNode singleRotateRight(TreeNode tree){ TreeNode k1=null; k1=tree.lson; tree.lson=k1.rson; k1.rson=tree; k1.rson.height=queryHeight(k1.rson); k1.height=queryHeight(k1); return k1;}/** * 单左旋,当右子树的高度-左子树的高度>1时调用 * @param k2 根节点 * @return 变换过后的根节点 */public TreeNode singleRotateLeft(TreeNode tree){ TreeNode k1=null; k1=tree.rson; tree.rson=k1.lson; k1.lson=tree; k1.lson.height=queryHeight(k1.lson); k1.height=queryHeight(k1); return k1;}/** * 考虑实际情况是否需要左旋,然后再进行右旋 * @param k3 根节点 * @return 变换过后的根基点 */public TreeNode doubleRotateLR(TreeNode tree){ tree.lson=singleRotateLeft(tree.lson); tree=singleRotateRight(tree); return tree;}/** * 考虑实际情况是否需要右旋,然后再进行左旋 * @param k3 根节点 * @return 变换过后的根基点 */public TreeNode doubleRotateRL(TreeNode tree){ tree.rson=singleRotateRight(tree.rson); tree=singleRotateLeft(tree); return tree;}/** * 判断节点是否需要进行平衡旋转,如需进行平衡旋转则进行旋转,否则不做变动返回 * @param tree 需要判断是否进行旋转的子树的根节点 * @return 返回旋转过后的根节点 */private TreeNode nodeRotate(TreeNode tree){ //进行平衡旋转 if(queryHeight(tree.lson)-queryHeight(tree.rson)>1){ if(queryHeight(tree.lson.lson)>queryHeight(tree.lson.rson)){ tree=singleRotateRight(tree); }else{ tree=doubleRotateLR(tree); } }else if(queryHeight(tree.rson)-queryHeight(tree.lson)>1){ if(queryHeight(tree.rson.rson)>queryHeight(tree.rson.lson)){ tree=singleRotateLeft(tree); }else{ tree=doubleRotateRL(tree); } } return tree;}/** * 插入节点 * @param tree 所要插入内容的子树的根节点 * @param data 所要插入数据所构成的数据节点 * @return 插入数据后的子数的根节点 */public TreeNode insertNode(TreeNode tree,TreeNode data){ if(tree==null) return data; //将节点插入树中 if(tree.data>data.data){ if(tree.lson==null){ tree.lson=data; }else{ tree.lson=insertNode(tree.lson,data); } }else if(tree.data<data.data){ if(tree.rson==null){ tree.rson=data; }else{ tree.rson=insertNode(tree.rson,data); } } //更新节点高度 tree.height=queryHeight(tree); //进行平衡旋转 tree=nodeRotate(tree); return tree;}/** * 从平衡二叉树中查找数据,并显示查找过程中遍历到的节点 * @param tree 二叉树的根节点 * @param data 所要查找的数据 */public void search(TreeNode tree,int data){ TreeNode temp; boolean flag=false; temp=tree; while(temp!=null){ if(temp.data==data){ flag=true; System.out.print(data+" "); break; }else if(data>temp.data){ System.out.print(temp.data+" "); temp=temp.rson; }else if(data<temp.data){ System.out.print(temp.data+" "); temp=temp.lson; } } if(flag) System.out.println("success"); else System.out.println("fail");}/** * 从二叉树中删除节点 * @param tree 二叉树的根节点 * @param data 所要删除的数据 * @return 删除节点后的二叉树的根节点 */public TreeNode deleteNode(TreeNode tree,int data){ ArrayList<TreeNode> nodes=new ArrayList<TreeNode>(); TreeNode temp; if(tree==null){ System.out.println("There is no number in the data!"); return null; } if(tree.height==1&&tree.data!=data){ System.out.println("There is no such node!"); return tree; } if(tree.data==data){ tree=deleteNodeOperate(tree); }else{ temp=tree; while(true){ nodes.add(temp); if(temp.height==1) break; if(temp.lson!=null&&temp.lson.data==data){ temp.lson=deleteNodeOperate(temp.lson); break; }else if(temp.rson!=null&&temp.rson.data==data){ temp.rson=deleteNodeOperate(temp.rson); break; }else if(data<temp.data){ temp=temp.lson; }else if(data>temp.data){ temp=temp.rson; } } } for(int i=nodes.size()-1;i>=0;i--){ nodes.get(i).height=queryHeight(nodes.get(i)); } for(int i=nodes.size()-1;i>=0;i--){ //进行平衡旋转 nodes.set(i, nodeRotate(nodes.get(i))); } return tree;}/** * 进行删除操作 * @param tree 所要删除的节点 * @return 删除所要删除节点后子树的根节点 */private TreeNode deleteNodeOperate(TreeNode tree){ ArrayList<TreeNode> nodes=new ArrayList<TreeNode>(); TreeNode temp; if(tree.height==1) return null; else{ nodes.add(tree); if(queryHeight(tree.lson)<queryHeight(tree.rson)){ temp=tree.rson; nodes.add(temp); while(temp.lson!=null){ temp=temp.lson; nodes.add(temp); } tree.data=temp.data; if(temp.rson==null){ if(temp==tree.rson){ tree.rson=null; }else{ nodes.get(nodes.size()-2).lson=null; } nodes.remove(nodes.size()-1); }else{ temp.lson=temp.rson.lson; temp.data=temp.rson.data; temp.rson=temp.rson.rson; } }else{ temp=tree.lson; nodes.add(temp); while(temp.rson!=null){ temp=temp.rson; nodes.add(temp); } tree.data=temp.data; if(temp.lson==null){ if(temp==tree.lson){ tree.lson=null; }else{ nodes.get(nodes.size()-2).rson=null; } nodes.remove(nodes.size()-1); }else{ temp.rson=temp.lson.rson; temp.data=temp.lson.data; temp.lson=temp.lson.lson; } } for(int i=nodes.size()-1;i>=0;i--){ nodes.get(i).height=queryHeight(nodes.get(i)); } for(int i=nodes.size()-1;i>=0;i--){ //进行平衡旋转 nodes.set(i, nodeRotate(nodes.get(i))); } } return tree;}public void showAll(TreeNode tree){ if(tree!=null){ if(tree.lson!=null) showAll(tree.lson); System.out.print(tree.data+" "); if(tree.rson!=null) showAll(tree.rson); }}public static void main(String[] args) { int len=100; TreeNode[] test=new TreeNode[len]; BalancedBinaryTree bbt=new BalancedBinaryTree(); ArrayList<Integer> nums=new ArrayList<Integer>(); Scanner in=new Scanner(System.in); System.out.println(); for(int i=0;i<len;i++){ test[i]=new TreeNode(); test[i].lson=null; test[i].rson=null; test[i].height=1; test[i].data=(int)Math.ceil(Math.random()*900+10); System.out.print(test[i].data+" "); bbt.root=bbt.insertNode(bbt.root, test[i]); } System.out.println(); bbt.showAll(bbt.root); System.out.println(); System.out.println(bbt.root.height); bbt.showAll(bbt.root); for(int i=0;i<9;i++){ bbt.deleteNode(bbt.root, in.nextInt()); System.out.println(); }}
}
- 平衡二叉树学习小节
- 数据结构学习-平衡二叉树
- 数据结构学习笔记-平衡二叉树实现
- 【平衡二叉树】SBT学习笔记
- [学习笔记]AVL平衡二叉树
- 随机二叉平衡树treap学习
- 平衡二叉树平衡法则
- 平衡二叉树(+二叉排列树)(学习记录)
- 二叉树--二叉平衡树
- 平衡二叉树的
- 平衡二叉树
- 平衡二叉树
- 平衡二叉树
- 平衡二叉树
- 平衡二叉查找树
- 平衡二叉树 详解
- 平衡二叉树
- 平衡二叉树
- jQuery的ajax详解
- 最大上升子序列和
- 解析波士顿动力Handle机器人背后的技术(附PPT+视频)
- 从1到n整数中1出现的次数
- MySQL的初次见面礼基础实战篇
- 平衡二叉树学习小节
- 创建型模式-抽象工厂模式(abstract_factory)
- 消息队列之ActiveMQ
- jdbc学习笔记
- ConcurrentHashMap
- 用选择法对10个整数排序
- Json数据解析在Unity3d中的应用
- Android5 Lollipop 中关于 SELinux 安全机制调试笔记
- maven创建struts2项目