平衡二叉树对于初学者一直是一个比较复杂的知识点,因为其里面涉及到了大量的旋转操作。把大量的同学都给转晕了。这篇文章最主要的特点就是通过动画的形式演示。确保大家都能看懂。最后是手写一个平衡二叉树。

一、概念

平衡二叉树是外国的两个大爷发明的。一开始发明的是二叉查找树。后来觉得不给力演化成了平衡二叉树。那什么是二叉查找树呢?我们给出一张图来看看:

看到这张图我们就会发现如下的特征。从每个节点出发,左边的节点一定小于右边的。但是你会发现这可以高低不平,看起来很不美观。于是慢慢的演化成了平衡二叉树。(当然不是因为美观演化的)。也就是说平衡二叉树的前提就是一颗二叉查找树

平衡二叉树定义(AVL):(1)它的左子树和右子树的深度之差(平衡因子)的绝对值不超过1,(2)它的左子树和右子树都是一颗平衡二叉树。

也就是说以上两条规则,只要破坏了一个就不是平衡二叉树了。比如说下面这张图。

上面这张图就是破坏了二叉查找树这一条规则。当然了还有一条规则。也就是他的高度只差不能超过1.

现在相信我们已经明白了什么是平衡二叉树。下面我们就来看看平衡二叉树的增删改查操作是怎么样的。

二、平衡二叉树的插入操作

我们先从最简单的入手,一步一步来。

1、右旋

首先我们插入几个数字,50,45,44。通过动画我们来演示一遍

(1)插入50根节点不会出现任何操作

(2)插入45,往左边插入即可

(3)插入44,破坏了平衡,于是右旋。

2、左旋

我们插入几个数字,50,60,70。通过动画我们来演示一遍

(1)插入50根节点不会出现旋转

(2)插入60,往右边插入即可

(3)插入70,破坏了平衡,于是左旋。

3、先右旋再左旋

我们依次插入50,60,55.通过动画我们演示一遍

(1)插入55,根节点,不会出现旋转

(2)插入60,往右边插入

(3)插入55,破坏了平衡,于是先把55和60右旋,然后整体左旋。

4、先左旋后右旋

我们依次插入50,40,45.通过动画我们演示一遍。

(1)插入55,根节点,不会出现旋转

(2)插入40,往左边插入

(3)插入45,破坏了平衡,于是先把45和40左旋,然后整体右旋。

现在我们基本上已经把插入的几种情况罗列出来了。现在我们画一张图,来一个总结。

上图对于每一种情况,从上往下看就好了。对于平衡二叉树的删除操作,其实也是同样的道理,找到相应的元素之后,对其进行删除,删除之后如果破坏了平衡,只需要按照上面的这几种情况进行调整即可。下面我们来分析一下平衡二叉树的查找操作。

三、平衡二叉树的查找

平衡二叉树的查找很简单,只需要按照二叉查找树的顺序执行就好。我们使用一张动画演示一下:

现在平衡二叉树的操作相信你已经能够理解。下面我们就来关注最后一个问题,那就是如何手写一颗平衡二叉树呢?

四、手写一颗平衡二叉树

平衡二叉树的代码操作,难点在于旋转。只要把旋转弄清楚基本上整个树就能完成了,根据上面旋转的特点我们从零开始定义一颗。

第一步:定义节点

 public class AVLNode {public int data;//保存节点数据public int depth;//保存节点深度public int balance;//是否平衡public AVLNode parent;//指向父节点public AVLNode left;//指向左子树public AVLNode right;//指向右子树
​public AVLNode(int data){this.data = data;depth = 1;balance = 0;left = null;right = null;}}

第二步:插入数据

public void insert(AVLNode root, int data){//如果说插入的数据小于根节点,往左边递归插入if (data < root.data){if (root.left != null){insert(root.left, data);}else {root.left = new AVLNode(data);root.left.parent = root;}}//如果说插入的数据小于根节点,往左边递归插入else {if (root.right != null){insert(root.right, data);}else {root.right = new AVLNode(data);root.right.parent = root;}}//插入之后,计算平衡银子root.balance = calcBalance(root);// 左子树高,应该右旋if (root.balance >= 2){// 右孙高,先左旋if (root.left.balance == -1){left_rotate(root.left);}right_rotate(root);}// 右子树高,左旋if (root.balance <= -2){// 左孙高,先右旋if (root.right.balance == 1){right_rotate(root.right);}left_rotate(root);}//调整之后,重新计算平衡因子和树的深度root.balance = calcBalance(root);root.depth = calcDepth(root);
}

第三步:左旋和右旋的调整

1、右旋

    // 右旋private void right_rotate(AVLNode p){// 一次旋转涉及到的结点包括祖父,父亲,右儿子AVLNode pParent = p.parent;AVLNode pLeftSon = p.left;AVLNode pRightGrandSon = pLeftSon.right;// 左子变父pLeftSon.parent = pParent;if (pParent != null){if (p == pParent.left){pParent.left = pLeftSon;}else if (p == pParent.right){pParent.right = pLeftSon;}}pLeftSon.right = p;p.parent = pLeftSon;// 右孙变左孙p.left = pRightGrandSon;if (pRightGrandSon != null){pRightGrandSon.parent = p;}p.depth = calcDepth(p);p.balance = calcBalance(p);pLeftSon.depth = calcDepth(pLeftSon);pLeftSon.balance = calcBalance(pLeftSon);}

2、左旋

    private void left_rotate(AVLNode p){// 一次选择涉及到的结点包括祖父,父亲,左儿子AVLNode pParent = p.parent;AVLNode pRightSon = p.right;AVLNode pLeftGrandSon = pRightSon.left;// 右子变父pRightSon.parent = pParent;if (pParent != null){if (p == pParent.right){pParent.right = pRightSon;}else if (p == pParent.left){pParent.left = pRightSon;}}pRightSon.left = p;p.parent = pRightSon;// 左孙变右孙p.right = pLeftGrandSon;if (pLeftGrandSon != null){pLeftGrandSon.parent = p;}p.depth = calcDepth(p);p.balance = calcBalance(p);pRightSon.depth = calcDepth(pRightSon);pRightSon.balance = calcBalance(pRightSon);}

第四步:计算平衡和深度

1、计算平衡

    public int calcBalance(AVLNode p){int left_depth;int right_depth;//左子树深度if (p.left != null){left_depth = p.left.depth;}else {left_depth = 0;}//右子树深度if (p.right != null){right_depth = p.right.depth;}else {right_depth = 0;}return left_depth - right_depth;}

2、计算深度

    public int calcDepth(AVLNode p){int depth = 0;if (p.left != null){depth = p.left.depth;}if (p.right != null && depth < p.right.depth){depth = p.right.depth;}depth++;return depth;}

看起来代码有些多,其实梳理一下就不多了。

(1)首先定义一个节点,里面有get和set方法,构造函数等等做准备工作

(2)直接写业务流程,比如说这里的insert操作,里面涉及到的旋转操作先用方法代替

(3)对主业务流程的操作,缺哪一个方法,写哪一个方法即可

ds查找—二叉树平衡因子_面试官让我手写一个平衡二叉树,我当时就笑了相关推荐

  1. 面试官让我手写一个生产者消费者模式?

    不知道你是否遇到过面试官让你手写生产者消费者代码.别说,前段时间有小伙伴还真的遇到了这种情况,当时是一脸懵逼. 但是,俗话说,从哪里跌倒就要从哪里爬起来.既然这次被问到了,那就回去好好研究一下,争取下 ...

  2. DS查找—二叉树平衡因子

    题目描述 二叉树用数组存储,将二叉树的结点数据依次自上而下,自左至右存储到数组中,一般二叉树与完全二叉树对比,比完全二叉树缺少的结点在数组中用0来表示. 计算二叉树每个结点的平衡因子,并按后序遍历的顺 ...

  3. 面试官让我手写一个RPC框架

    如今,分布式系统大行其道,RPC 有着举足轻重的地位.Dubbo.Thrift.gRpc 等框架各领风骚,学习RPC是新手也是老鸟的必修课.本文带你手撸一个rpc-spring-starter,深入学 ...

  4. 面试官:请手写一个带取消功能的延迟函数,axios 取消功能的原理是什么

    大家好,我是若川.最近组织了源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步.同时极力推荐订阅我写的<学习源码整体架构系列> ...

  5. 面试官系统精讲Java源码及大厂真题 - 26 惊叹面试官:由浅入深手写队列

    26 惊叹面试官:由浅入深手写队列 人生的价值,并不是用时间,而是用深度去衡量的. 引导语 现在不少大厂面试的时候会要求手写代码,我曾经看过一个大厂面试时,要求在线写代码,题目就是:在不使用 Java ...

  6. 当面试官让你手写防抖、节流时,是在考察什么

    防抖.节流,都是用于节省函数调用次数的方案,达到优化程序.提升性能甚至是避免bug的目的.作为一个经典的主题,也是面试常考项,部分面试官会让你手写,这时, 他是在考察什么?你能轻易地写出比较好的防抖. ...

  7. 当面试官让你手写flat,是在考察什么

    在面试者中,偶尔会遇到手写flat实现的面试题,如果基础不牢,或者没有准备,就容易挂.那么,这道典型的手写题,主要考察了些什么呢?本文一层层地剖析,让你豁然开朗. 考察点1: 熟不熟悉这个api 要想 ...

  8. 二叉树的创建_大多数人都不会手写创建并遍历二叉树,一航这里帮你终结了

    创建二叉树.遍历二叉树.二叉树的最近公共祖先任何疑问.意见.建议请公众号留言或联系qq474356284先序.后序创建二叉树先中后层序遍历二叉树二叉树的最近公共祖先 输入格式: 创建二叉树时的输入: ...

  9. 面试官让我手写队列,差点没写出来,回来后赶忙把重点记下来

    微信搜一搜[bigsai]更多精彩 点赞的帅哥美女祝你们越学越猛 目录 前言 队列介绍 普通队列 循环队列(数组实现) 循环队列(链表实现) 双向队列(加餐) 总结 前言 栈和队列是一对好兄弟,前面我 ...

最新文章

  1. 博士Nature发文:研究生阶段,4点经验助你学术“独立”!
  2. [原创]一种自动地将继承自NSObject的自定义类序列化成JSON的方法
  3. MyBatis框架学习:<select>节点中的resultType和resultMap属性
  4. rhel5.1 vncserver
  5. python读取excel一列-python读取excel(xlrd)
  6. 深入理解计算机系统 视频教程,深入理解计算机系统1
  7. 腾讯视频下载电脑_腾讯视频如何设置允许腾讯视频驻留功能
  8. 记录linux历史命令,如何将Linux系统的历史操作命令删除,并不再记录
  9. FFMPEG结构体分析:AVStream
  10. SQL Server 透视与逆透视转换解析
  11. php radio用法,JavaScript_JQuery radio(单选按钮)操作方法汇总,随着Jquery的作用越来越大,使 - phpStudy...
  12. 启用文件系统缓存,提高Tuxera NTFS运作性能
  13. python3 下载网络图片
  14. html中在线取色器,在线取色器(ColorPicker)的制造方式
  15. U盘PE安装原版Win10系统
  16. SPSS时序全局主成分分析方法
  17. java jxls导出excel
  18. SOPCAST所有频道的地址
  19. 学计算机拼音不好怎么办,孩子拼音基础差怎么办?告诉你学拼音技巧!
  20. 组装台式计算机需要哪些硬件,电脑硬件有哪些?组装一台电脑需要哪些配件详解...

热门文章

  1. 1.12 foreach循环遍历Collection集合
  2. Dubbo服务调用失败
  3. Synchronize读脏解决
  4. Java集合LinkedHashMap
  5. MySQL为表的所有字段添加数据
  6. CSS设置图片的重复
  7. docker tag 删除images_深入浅出 Docker (二) —— Docker的基本概念和架构原理
  8. anaconda+python3.6利用命令安装BeautifulSoup4-4.6.0
  9. emulator: ERROR: x86 emulation currently requires hardware acceleration!
  10. UI组件之 ProgressBar及其子类(一)ProgressBar进度条的使用