近期在学习数据结构上关于平衡二叉树的知识,看了严老师的思路,感觉用java写出递归的构建方式有点困难,由于当中的递归须要把引用传进去,所以感觉是要实现起来比較麻烦,所以就首先想到使用非递归的方式来实现构建平衡二叉树。

使用非递归的方式,思路也非常easy,就是为每个结点都要定义一个平衡因子的属性,当成功向树中插入一个数据时,我就要进行回溯,看看有没有平衡因子的绝对值等于2的结点,假设有,那就须要进行旋转。当时的思路仅限于这些,接触java没有多久,目測假设实现起来,有点困难。

所以,上网查询了一个博客写的代码,虽然不知道原作者是谁,只是还是贴上我參考的博客地址:http://blog.csdn.net/zxman660/article/details/7940190

当中,我觉得另一个难题就是,我定义的结点使用了泛型,即结点的value也是使用的泛型<E>,所以在比較大小的时候,我就无从下手,可能是我接触java不太久的原因,当我參考上述博客的思路后,竟然是这样实现的。

void compare(E element, Node node){
Comparable<? super E> e = (Comparable<? super E>)element;
int cmp = e.compareTo(node.element);
if(cmp > 0) //大于
else if(cmp == 0) //等于
else //小于
}

通过此次学习,尽管接触了平衡二叉树的构建,但还是感觉学到了不少的知识,而且对java的使用又有了很多其它的认识。

有了上述的感悟,感觉还是写一个博客来收藏一下,等自己有时间,再补充一下结点删除的代码。

可能本人代码在凝视上提供的思路有限,假设有看不懂的地方,能够參考上面的博客地址的内容。

package util;

/**
* 平衡二叉树
* 定义:首先它是一种特殊的二叉排序树,其次它的左子树和右子树都是平衡二叉树,
* 且左子树和右子树的深度之差不超过1
* 平衡因子:能够定义为左子树的深度减去右子树的深度
*
* 平衡二叉树是对二叉排序树的优化,防止二叉排序树在最坏情况下平均查找时间为n,
* 二叉排序树在此时形如一个单链表
* 平衡二叉树查找元素的次数不超过树的深度,时间复杂度为logN
*/ public class AVLTree<E> { /**根节点*/
private Node<E> root = null; /**树中元素的个数*/
private int size = 0; private static final int LEFT_HIGH = 1;
private static final int RIGHT_HIGH = -1;
private static final int EQUAL_HIGH = 0; public AVLTree(){} public boolean insertElement(E element){ Node<E> t = root;
if(t == null){
/**将值复制给根节点,其父节点为空*/
root = new Node(element, null);
size = 1;
return true;
} int cmp = 0;
Node<E> parent; /**用来保存t的父节点*/ /**查找结点应存放的位置*/
Comparable<? super E> e = (Comparable<? super E>)element;
do{
parent = t;
cmp = e.compareTo(t.element);
if(cmp < 0){
t = t.left;
}else if(cmp > 0){
t = t.right;
}else{
return false;
}
}while(t != null); /**将结点存放在对应的位置*/
Node<E> child = new Node(element, parent);
if(cmp < 0){
parent.left = child;
}else{
parent.right = child;
} /**開始回溯,为根节点改动balabce,以便进行对应的调整*/
while(parent != null){
cmp = e.compareTo(parent.element);
if(cmp < 0){
parent.balance ++;
}else{
parent.balance --;
} if(parent.balance == 0){/**从这以上的结点都不须要改动了,也不须要旋转了*/
break;
} if(Math.abs(parent.balance) == 2){
fixAfterInsertion(parent);
break;
}
parent = parent.parent;
}
size ++;
return true;
} private void fixAfterInsertion(Node<E> p) {
if(p.balance == 2){
leftBanance(p);
}
if(p.balance == -2){
rightBalance(p);
}
} /**
* 左平衡操作,即结点t的不平衡是由于左子树过深
*
* 1、假设新的结点插入到p的左孩子的左子树中,则直接进行右旋操作就可以
* t lc
* / \ 右旋操作 / \
* lc rc -------------> lcl t
* / \ / / \
* lcl lcr lcll lcr rc
* /
* lcll
*
* 2、假设新的结点插入到p的左孩子的右子树中,则须要进行分情况讨论
*
* 情况a:当p的左孩子的右子树根节点的balance = RIGHT_HIGH
*
* 1 1 4
* / \ / \ / \
* 2 6 左旋 4 6 右旋 2 1
* / \ -------> / \ --------> / / \
* 3 4 2 5 3 5 6
* \ /
* 5 3
*
*
* 情况b:当p的左孩子的右子树根节点的balance = LEFT_HIGH
*
* 1 1 4
* / \ / \ / \
* 2 6 左旋 4 6 右旋 2 1
* / \ -------> / --------> / \ \
* 3 4 2 3 5 6
* / / \
* 5 3 5
*
* 情况c:当p的左孩子的右子树根节点的balance = EQUAL_HIGH
*
* 1 1 4
* / \ / \ / \
* 2 7 左旋 4 7 右旋 2 1
* / \ -------> / \ --------> / \ / \
* 3 4 2 6 3 5 6 7
* / \ / \
* 5 6 3 5
* */ private void leftBanance(Node<E> t) { Node<E> lc = t.left;
switch(lc.balance){
case LEFT_HIGH: /**新结点插入到t的左孩子的左子树上,须要单右旋处理*/
lc.balance = EQUAL_HIGH;
t.balance = EQUAL_HIGH;
break; case RIGHT_HIGH: /**新结点插入到t的左孩子的右子树上,须要双旋处理*/
Node<E> rd = lc.right;
switch(rd.balance){
case LEFT_HIGH:
lc.balance = EQUAL_HIGH;
t.balance = RIGHT_HIGH;
break; case RIGHT_HIGH:
lc.balance = LEFT_HIGH;
t.balance = EQUAL_HIGH;
break;
case EQUAL_HIGH:
t.balance = EQUAL_HIGH;
lc.balance = EQUAL_HIGH;
break;
}
rd.balance = EQUAL_HIGH;
/**对t的左子树进行左旋处理*/
left_Rotate(t.left);
/**对t进行右旋处理*/
right_Rotate(t);
break;
}
} /**
* 右平衡操作,即结点t的不平衡是由于右子树过深
*
* 1、假设新的结点插入到p的右孩子的右子树中,则直接进行左旋操作就可以
*
* p r
* / \ / \
* l r 左旋操作 p rr
* / \ -----------> / \ \
* rl rr l rl rrr
* \
* rrr
*
*
* 2、假设新的结点插入到p的右孩子的左子树中,则须要进行分情况讨论
*
* 情况a:当p的右孩子的左子树根节点的balance = LEFT_HIGH
*
* 1 1 4
* / \ / \ / \
* 2 3 右旋 2 4 左旋 1 3
* / \ -------> / \ -------> / \ \
* 4 5 6 3 2 6 5
* / \
* 6 5
*
* 情况b:当p的右孩子的左子树根节点的balance = RIGHT_HIGH
*
* 1 1 4
* / \ / \ / \
* 2 3 右旋 2 4 左旋 1 3
* / \ -------> \ -------> / / \
* 4 5 3 2 6 5
* \ / \
* 6 6 5
*
*
* 情况C:当p的右孩子的左子树根节点的balance = EQUAL_HIGH
* 1 1 4
* / \ / \ / \
* 2 3 右旋 2 4 左旋 1 3
* / \ -------> / \ -------> / \ / \
* 4 5 6 3 2 6 7 5
* / \ / \
* 6 7 7 5
* */
private void rightBalance(Node<E> p) {
Node<E> rc = p.right;
switch(rc.balance){
case RIGHT_HIGH: /**新结点插入到t的右孩子的右子树上,须要单左旋处理*/
rc.balance = EQUAL_HIGH;
p.balance = EQUAL_HIGH;
break; case LEFT_HIGH: /**新结点插入到t的右孩子的左子树上,须要双旋处理*/
Node<E> ld = rc.left;
switch(ld.balance){
case LEFT_HIGH:
p.balance = EQUAL_HIGH;
rc.balance = RIGHT_HIGH;
break;
case RIGHT_HIGH:
p.balance = LEFT_HIGH;
rc.balance = EQUAL_HIGH;
break;
case EQUAL_HIGH:
p.balance = EQUAL_HIGH;
rc.balance = EQUAL_HIGH;
break;
}
ld.balance = EQUAL_HIGH;
/**对p的右子树进行右旋处理*/
right_Rotate(p.right);
/**对p进行左旋处理*/
left_Rotate(p);
break;
} } /**
* 左旋操作
* p r
* / \ / \
* l r 左旋操作 p rr
* / \ -----------> / \ \
* rl rr l rl rrr
* \
* rrr
* */ private void left_Rotate(Node<E> p) {
if(p != null){
Node<E> r = p.right; /**获得p的右子树的根节点r*/ p.right = r.left; /**将r的左子树转接到p的右子树上*/
if(r.left != null){ /**假设r的左子树不为空,将左子树的父节点设置为p*/
r.left.parent = p;
} r.parent = p.parent; /**改动r的父节点,改动为p的父节点*/
if(p.parent == null){ /**假设p的父节点为null,那么如今r就是根节点了*/
root = r;
}else if(p == p.parent.left){/**假设p为其父节点的左孩子,将其父节点的左孩子指向r*/
p.parent.left = r;
}else if(p == p.parent.right){/**假设p为其父节点的右孩子,将其父节点的右孩子指向r*/
p.parent.right = r;
} r.left = p; /**将r的左孩子设置为p*/
p.parent = r; /**将p的父节点设置为r*/
}
} /**
* 右旋操作
*
* p l
* / \ 右旋操作 / \
* l r -------------> ll p
* / \ / / \
* ll lr lll lr r
* /
* lll
* */
private void right_Rotate(Node<E> p) { if(p != null){
Node<E> l = p.left; /**获取p的左孩子l*/ p.left = l.right; /**将l的右子树变为p的左子树*/
if(l.right != null){ /**假设l的右子树不为空,将其父节点设置为p*/
l.right.parent = p;
} l.parent = p.parent; /**将r的父节点改动为p的父节点*/
if(p.parent == null){ /**假设p的父节点为null,即l为root*/
root = l;
}else if(p == p.parent.left){ /**假设p为其父节点的左孩子,将p的父节点的左孩子指向l*/
p.parent.left = l;
}else if(p == p.parent.right){ /**假设p为其父节点的右孩子,将p的父节点的右孩子指向l*/
p.parent.right = l;
} l.right = p; /**将l的右子树变为p*/
p.parent = l; /**改动p的父节点为l*/
}
} /**中序非递归方式遍历平衡二叉树*/
public void nrInOrderTraverse(){
Stack<Node<E>> stack = new Stack<Node<E>>();
Node<E> p = root;
while(p != null || !stack.isEmpty()){
while(p != null){
stack.push(p);
p = p.left;
}
p = stack.pop();
System.out.println(p.element);
p = p.right;
}
} /**平衡二叉树的结点定义*/
class Node<E>{
E element;
/**结点的平衡因子*/
int balance = 0;
/**左孩子结点、右孩子结点、父节点*/
Node<E> left;
Node<E> right;
Node<E> parent; public Node(){}
public Node(E element, Node<E> parent){
this.element = element;
this.parent = parent;
} public String toString(){
return element + " BF=" + balance;
} } public static void main(String[] args) {
Integer[] num = {5,8,2,0,1, -2, -9, 100};
AVLTree<Integer> avl = new AVLTree<Integer>();
for(int i = 0; i < num.length; i++){
avl.insertElement(num[i]);
}
avl.nrInOrderTraverse();
} }

Java实现平衡二叉树(AVLTree)的构建的更多相关文章

  1. 面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序

    面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序 Ajax 为更好的 Web 应用程序铺平了道路 在 Web 应用程序开发中,页面重载循环是最大的一个使用障碍,对于 Java™ ...

  2. 【JAVA-JDT-AST】Java抽象语法树的构建、遍历及转成dot格式(附Github源码)

    Background: 最近为了重现tree-based clone detection的论文:L. Jiang, G. Misherghi, Z. Su, and S. Glondu. Deckar ...

  3. Java秒杀系统实战系列~构建SpringBoot多模块项目

    摘要:本篇博文是“Java秒杀系统实战系列文章”的第二篇,主要分享介绍如何采用IDEA,基于SpringBoot+SpringMVC+Mybatis+分布式中间件构建一个多模块的项目,即“秒杀系统”! ...

  4. java——平衡二叉树 AVLTree、AVLMap、AVLSet

    平衡二叉树:对于任意一个节点,左子树和右子树的高度差不能超过1 package Date_pacage; import java.util.ArrayList; public class AVLTre ...

  5. 用gulp打包带参数资源做法与asp.net/java项目结合的自动构建方案探讨

    先探讨方案,后续再实现. gulp打包前端教程配置:http://www.cnblogs.com/EasonJim/p/6209951.html 可能存在以下场景: 1.整个服务端采用接口的形式暴露给 ...

  6. Eclipse的Java工作集和多工程构建路径

    一.Java工作集: Eclipse有一个小功能,就是创建Java Working Set.它的作用是解决Package Explorer窗格中创建很多工程时出现拥挤的麻烦. 在创建(New对话框)时 ...

  7. Jenkins日常运维笔记-重启数据覆盖问题、迁移、基于java代码发版(maven构建)

    之前在公司机房部署了一套jenkins环境,现需要迁移至IDC机房服务器上,迁移过程中记录了一些细节:1)jenkins默认的主目录放在当前用户家目录路径下的.jenkins目录中.如jenkins使 ...

  8. 利用Google开源Java容器化工具Jib构建镜像

    转载:https://blog.csdn.net/u012562943/article/details/80995373 一.前言 容器的出现让Java开发人员比以往任何时候都更接近“编写一次,到处运 ...

  9. Java编码 蛇形矩阵的构建与遍历输出

    一.蛇形矩阵的构建,并按行输出 例: 输入:n, 生成n*n的蛇形矩阵 1 2 3 8 9 4 7 6 5 输出:1 2 3 8 9 4 7 6 5 java编码 public static void ...

随机推荐

  1. CAS单点登录(SSO)完整教程

    转:http://blog.csdn.net/frinder/article/details/7969925 CAS单点登录(SSO)完整教程(2012-02-01更新) 一.教程说明 前言 教程目的 ...

  2. Action类一般情况为何要继承ActionSupport

    struts2中的action可以继承ActionSupport,也可以不继承ActionSupport.不继承ActionSupport的情况只需要有一个方法,返回String,即可,记住,在继承A ...

  3. iPhone开发 - 常用库

    iPhone开发 - 常用库 这里总结了iPhone开发者开发过程中可能需要的一些资源 如何用Facebook graphic api上传视频: http://developers.facebook. ...

  4. Angular 2 npm start 报错

    首先, index.html 和styles.css是和app目录平级的, 不要扔到里面去, 否则会404 确认配置文件齐全, 路径都正确之后 npm start What?! 照着快速起步也会弄错吗 ...

  5. 提升网站用户体验—WebP 图片的高效使用

    一.WebP 的由来 现代图像压缩技术对我们的生活方式影响很大.数码相机能将上千张高质量图片存储到一张内存卡里.智能手机可以与邻近设备快速分享高分辨率的图片.网站与手机等移动设备能快速展示各种富媒体. ...

  6. RSA算法原理(二)

    上一次,我介绍了一些数论知识. 有了这些知识,我们就可以看懂RSA算法.这是目前地球上最重要的加密算法. 六.密钥生成的步骤 我们通过一个例子,来理解RSA算法.假设爱丽丝要与鲍勃进行加密通信,她该怎 ...

  7. VS2013中直接浏览网页显示“无法显示此页”的可能原因

    今天在VS2013里面新建了一个空的web应用程序WebApplication1,然后新建了一个 WebForm1.aspx 直接浏览WebForm1.aspx后,网页直接显示: 在网上查询了写资料, ...

  8. 14.6.1 InnoDB Startup Configuration 启动配置

    14.6.1 InnoDB Startup Configuration 启动配置 首先描述关于InnoDB 配置设计数据库文件,日志文件,page size 和内存buffer 的配置. 推荐你定义数 ...

  9. Bluetooth LE(低功耗蓝牙) - 第四部分

    回顾 在本系列前几篇文章中我们完成了BLE设备的发现 , 为我们的app通过BLE显示从TI SensorTag设备中获取到环境温度和湿度的工作打下了基础.在这篇文章中我们将着眼于连接到我们所发现的S ...

  10. Linux Kernel ‘perf’ Utility 本地提权漏洞

    漏洞名称: Linux Kernel ‘perf’ Utility 本地提权漏洞 CNNVD编号: CNNVD-201309-050 发布时间: 2013-09-09 更新时间: 2013-09-09 ...