二叉排序树与平衡二叉树(AVL)

首先明白我们为什么使用二叉树来存储,并进行查找,它和我们通常的链式存储和线性存储有什么优势,分析完毕之后,我们再学习二叉树就很有针对性。

  1. 对于数组来说,我们查找数据替换数据很方便,但是遇到插入数据和删除数据来说就很麻烦。
  2. 对于单链表来说,方便进行插入和删除,但是查找的话就很不方便。基本事件复杂度都在O(n)
  3. 对于二叉树来说,用它存储的数据对于查询和删除,时间复杂度得到很大的提升。对于二叉排序树,如根节点的左右孩子的高度相差为零的话,查找的效率和数组的二分查找差不多。对于二叉树结点的删除来说,要进行分情况讨论,本文暂时不解释。

二叉排序树的构建规则

  1. 给出要进行构建二叉树的数组 int[] arr = {21,3,11,2,13,4,5}
  2. 添加结点时,如果添加的结点大于当前结点(当前结点没有右孩子)就往该结点的右子树上进行添加;如果要添加的结点小于当前结点(当前结点没有左孩子)就往该节点的左子树上进行添加。
    示例图
    在这里插入图片描述
    具体的构建代码如下
    树的结点类
class NodeSort{
    int value;
    NodeSort left;
    NodeSort right;
    public NodeSort(int value) {
        this.value = value;
    }
    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }
    public void middleOrder(){//中序遍历
        if(this.left!=null){
            this.left.middleOrder();
        }
        System.out.println(this.toString());
        if(this.right!=null){
            this.right.middleOrder();
        }
    }
    protected void addNode(NodeSort node){//核心算法
        if(this.value>node.value){
            if(this.left!=null) {
                this.left.addNode(node);
            }else{
                this.left=node;
            }
        }else{
            if(this.right!=null){
                this.right.addNode(node);
            }else{
                this.right=node;
            }
        }
    }
 }

二叉排序树里面的方法

class BinarySort{
    NodeSort root;
    //添加结点
    public static void addNode(NodeSort node){
        if(root==null){
            root = node;
        }else{
            root.addNode(node);
        }
    }
    //中序遍历
    public static void moiddleSort(){
		if(root!=null){
			root.middleOrder();
		}else{
			System.out.println("无结点值");
		}
	}
     public static void main(String[] args) {
        int[] arr = {21,3,11,2,13,4,5}
        for(int i =0;i<arr.length;i++){
            addNode(new NodeSort(arr[i]));
        }
    }
}

总结 对于上述构建的二叉排序树,我们发现根节点没有右子树,而且该二叉树的高度很高。
二叉树的高度越高,查询的效率就会变慢。因此我们需要经该二叉树进行平衡化处理,以此来提高二叉树的查找效率。

平衡二叉树

右旋

示例数组int[] arr ={10,12,8,9,7,6};对应的二叉排序树如下
在这里插入图片描述
右旋思想

  1. 如果在添加结点的时候,根节点的左子树的高度减去根节点的右子树的高度大于1的话就进行右旋
  2. 当前结点的值重新构建一个新的结点(调用addNode(NodeSort node)方法的对象)在这里root调用的addNode方法,因此当前结点就是root NodeSort newnode = new NodeSort(value)– value是root的结点值。
  3. 新结点的右子树是当前结点的右子树 newnode.right=right
  4. 新结点的左子树是当前结点的左子树的右子树 newnode.left = left.right
  5. 当前结点的值是当前结点左子树的值 value = left.value
  6. 左子树等于左子树的左子树 left = left.left
  7. 右子树是新节点 right= newnode
    右旋调整后的二叉树是
    在这里插入图片描述
左旋

示例数组int[] arr ={4,3,6,5,7,8}对应的二叉排序树如下
在这里插入图片描述
左旋思想

  1. 如果在添加结点的时候,根节点的右子树的高度减去根节点的左子树的高度大于1的话就进行左旋
  2. 当前结点的值重新构建一个新的结点(调用addNode(NodeSort node)方法的对象)在这里root调用的addNode方法,因此当前结点就是root NodeSort newnode = new NodeSort(value)– value是root的结点值。
  3. 新结点的左子树是当前结点的左子树 newnode.left=left
  4. 新结点的右子树是当前结点的右子树的左子树 newnode.right = right.left
  5. 当前结点的值是当前结点右子树的值 value = right.value
  6. 右子树等于右子树的右子树 right= right.right
  7. 左子树是新节点 left= newnode

在这里插入图片描述
但是我们有时会遇到这样的情况,左旋或者右旋都无法达到平衡二叉树
例如如下。
int[] arr ={10,11,7,6,8,9};
在这里插入图片描述
这样的情况,因此在进行旋转的时候需要加上一条判断
如果左子树的高度减去右子树的高度大于1的话
如果左子树的左子树的高度小于左子树的右子树的高度,那么要对该左子树进行左旋处理,最后再进行右旋处理
如果右子树的高度减去左子树的高度大于1的话
如果右子树的右子树的高度小于右子树的左子树的高度,那么要对该右子树进行右旋处理,最后再进行左旋处理

package com.sofency.Tree;
/**
 * @auther sofency
 * @date 2020/3/8 14:30
 * @package com.sofency.Tree
 * @description 平衡二叉树
 */
public class AVLTree {
    public static  NodeTest root;//根节点
    public static void main(String[] args) {
//        int[] arr ={4,3,6,5,7,8};
//      int[] arr ={10,12,8,9,7,6};
//      int[] arr ={10,11,7,6,8,9};
        int[] arr = {21,3,11,2,13,4,5};
        for(int i=0;i<arr.length;i++){
            addNode(new NodeTest(arr[i]));
        }
        System.out.println("平衡处理");
        System.out.println("左孩子高度"+root.leftHeight());
        System.out.println("右孩子高度"+root.rightHeight());
        middleSort();//中序遍历输出
    }

    //中序遍历
    public static void middleSort(){
        if(root==null){
            return;
        }else{
            root.middleOrder();//中序遍历
        }
    }

    public static void addNode(NodeTest node){
        if(root==null){
            root=node;
        }else{
            root.addNodes(node);
        }
    }
}
class NodeTest implements Comparable{
    int value;
    NodeTest left;
    NodeTest right;

    public NodeTest(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }

    //左孩子的结点
    public int leftHeight(){
        if(left==null){
            return 0;
        }
        return left.height();
    }

    //右孩子的高度
    public int rightHeight(){
        if(right==null){
            return 0;
        }
        return right.height();
    }

    //计算结点的深度 因为每个节点也可以简介的看错是一个是
    public int height(){//返回深度
        return Math.max(left==null?0:left.height(),right==null?0:right.height())+1;
    }

    public void middleOrder(){
        if(this.left!=null){
            this.left.middleOrder();
        }
        System.out.println(this.toString());
        if(this.right!=null){
            this.right.middleOrder();
        }
    }

    //左旋
    public void leftRotate(){
        //1. 获取当前结点的值构建新结点
        NodeTest nodeTest = new NodeTest(value);//
        //新节点的左孩子连接的是当前结点的左孩子
        nodeTest.left=left;
        //新节点的右孩子是当前结点的右孩子的左孩子
        nodeTest.right=right.left;

        //当前的值是右孩子的值
        value=right.value;
        //右孩子是右孩子的的右孩子
        right=right.right;
        //左孩子是新构建的结点
        left = nodeTest;
    }

    //右旋
    public void rightRotate(){
        NodeTest nodeTest = new NodeTest(value);
        nodeTest.right=right;
        nodeTest.left=left.right;
        value=left.value;
        left =left.left;
        right=nodeTest;
    }

    //添加结点
    public void addNodes(NodeTest node){
        if(this.value>node.value){
            if(this.left!=null){
                this.left.addNodes(node);
            }else{
                this.left=node;
            }
        }else {
            if (this.right != null) {
                this.right.addNodes(node);
            } else {
                this.right = node;
            }
        }
        if(rightHeight()-leftHeight()>1){//如果右孩子的高度减去左孩子的高度大于1进行左旋转
            if(right!=null&&(right.leftHeight()>right.rightHeight())){
                right.rightRotate();
            }
            leftRotate();
            return;
        }
        //如果左孩子的高度-右孩子的高度大于1
        if(leftHeight()-rightHeight()>1){//如果右孩子的高度减去左孩子的高度大于1进行左旋转
            //如果左孩子的左孩子
            if(left!=null&&(left.rightHeight()>left.leftHeight())){//如果左孩子的左孩子的高度小于左孩子的右孩子的高度 要进行左旋
                left.leftRotate();
            }
            rightRotate();
        }
    }
    @Override
    public int compareTo(Object o) {
        return this.value-((Node)o).value;
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1本程序在vc++6.0编译通过并能正常运行。 2主界面 程序已经尽量做到操作简便了,用户只需要根据提示一步步进行操作就行了。 六思考和总结: 这个课程设计的各个基本操作大部分都在我的综合性实验中实现了,所以做这个主要攻克插入和删除这两个算法!其中插入在书本上已经有了,其中的右平衡算法虽然没有给出,但通过给出的左平衡算法很容易就可以写出右平衡算法。所以最终的点就在于删除算法的实现!做的过程中对插入算法进行了非常非常多次的尝试!花了非常多的时间,这其中很多时候是在对程序进行单步调试,运用了VC6。0的众多良好工具,也学到了很多它的许多好的调试手段。 其中删除算法中最难想到的一点是:在用叶子结点代替要删除的非叶子结点后,应该递归的运用删除算法去删除叶子结点!这就是整个算法的核心,其中很强烈得体会到的递归的强大,递归的最高境界(我暂时能看到的境界)! 其它的都没什么了。选做的那两个算法很容易实现的: 1合并两棵平衡二叉排序树:只需遍历其中的一棵,将它的每一个元素插入到另一棵即可。 2拆分两棵平衡二叉排序树:只需以根结点为中心,左子树独立为一棵,右子树独立为一棵,最后将根插入到左子树或右子树即可。 BSTreeEmpty(BSTree T) 初始条件:平衡二叉排序树存在。 操作结果:若T为空平衡二叉排序树,则返回TRUE,否则FALSE. BSTreeDepth(BSTree T) 初始条件:平衡二叉排序树存在。 操作结果:返回T的深度。 LeafNum(BSTree T) 求叶子结点数,非递归中序遍历 NodeNum(BSTree T) 求结点数,非递归中序遍历 DestoryBSTree(BSTree *T) 后序遍历销毁平衡二叉排序树T R_Rotate(BSTree *p) 对以*p为根的平衡二叉排序树作右旋处理,处理之后p指向新的树根结点 即旋转处理之前的左子树的根结点 L_Rotate(BSTree *p) 对以*p为根的平衡二叉排序树作左旋处理,处理之后p指向新的树根结点, 即旋转处理之前的右子树的根结点 LeftBalance(BSTree *T) 对以指针T所指结点为根的平衡二叉排序树作左平衡旋转处理, 本算法结束时,指针T指向新的根结点 RightBalance(BSTree *T) 对以指针T所指结点为根的平衡二叉排序树作右平衡旋转处理, 本算法结束时,指针T指向新的根结点 Insert_AVL(BSTree *T, TElemType e, int *taller) 若在平衡的二叉排序树T中不存在和e有相同的关键字的结点, 则插入一个数据元素为e的新结点,并返回OK,否则返回ERROR. 若因插入而使二叉排序树失去平衡,则作平衡旋转处理 布尔变量taller反映T长高与否 InOrderTraverse(BSTree T) 递归中序遍历输出平衡二叉排序树 SearchBST(BSTree T, TElemType e, BSTree *f, BSTree *p) 在根指针T所指的平衡二叉排序树中递归的查找其元素值等于e的数据元素, 若查找成功,则指针p指向该数据元素结点,并返回TRUE,否则指针p 指向查找路径上访问的最后一个结点并返回FALSE,指针f指向T的双亲, 其初始调用值为NULL Delete_AVL(BSTree *T, TElemType e, int *shorter) 在平衡二叉排序树中删除元素值为e的结点,成功返回OK,失败返回ERROR PrintBSTree_GList(BSTree T) 以广义表形式打印出来 PrintBSTree_AoList(BSTree T, int length) 以凹入表形式打印,length初始值为0 Combine_Two_AVL(BSTree *T1, BSTree T2) 合并两棵平衡二叉排序树 Split_AVL(BSTree T, BSTree *T1, BSTree *T2) 拆分两棵平衡二叉树 } (2)存储结构的定义: typedef struct BSTNode { TElemType data; int bf; //结点的平衡因子 struct BSTNode *lchild, *rchild;//左.右孩子指针 }BSTNode, *BSTree;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值