34.AVL树
1.创建Node结点
class Node {
int value;
Node left;
Node right;
public Node(int value) {
this.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;
}
//左旋转方法
private void leftRotate() {
//创建新的结点,以当前根结点的值
Node newNode = new Node(value);
//把新的结点的左子树设置成当前结点的左子树
newNode.left = left;
//把新的结点的右子树设置成带你过去结点的右子树的左子树
newNode.right = right.left;
//把当前结点的值替换成右子结点的值
value = right.value;
//把当前结点的右子树设置成当前结点右子树的右子树
right = right.right;
//把当前结点的左子树(左子结点)设置成新的结点
left = newNode;
}
//右旋转
private void rightRotate() {
Node newNode = new Node(value);
newNode.right = right;
newNode.left = left.right;
value = left.value;
left = left.left;
right = newNode;
}
// 查找要删除的结点
/**
*
* @param value
* 希望删除的结点的值
* @return 如果找到返回该结点,否则返回null
*/
public Node search(int value) {
if (value == this.value) { // 找到就是该结点
return this;
} else if (value < this.value) {// 如果查找的值小于当前结点,向左子树递归查找
// 如果左子结点为空
if (this.left == null) {
return null;
}
return this.left.search(value);
} else { // 如果查找的值不小于当前结点,向右子树递归查找
if (this.right == null) {
return null;
}
return this.right.search(value);
}
}
// 查找要删除结点的父结点
/**
*
* @param value
* 要找到的结点的值
* @return 返回的是要删除的结点的父结点,如果没有就返回null
*/
public Node searchParent(int value) {
// 如果当前结点就是要删除的结点的父结点,就返回
if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
return this;
} else {
// 如果查找的值小于当前结点的值, 并且当前结点的左子结点不为空
if (value < this.value && this.left != null) {
return this.left.searchParent(value); // 向左子树递归查找
} else if (value >= this.value && this.right != null) {
return this.right.searchParent(value); // 向右子树递归查找
} else {
return null; // 没有找到父结点
}
}
}
@Override
public String toString() {
return "Node [value=" + value + "]";
}
// 添加结点的方法
// 递归的形式添加结点,注意需要满足二叉排序树的要求
public void add(Node node) {
if (node == null) {
return;
}
// 判断传入的结点的值,和当前子树的根结点的值关系
if (node.value < this.value) {
// 如果当前结点左子结点为null
if (this.left == null) {
this.left = node;
} else {
// 递归的向左子树添加
this.left.add(node);
}
} else { // 添加的结点的值大于 当前结点的值
if (this.right == null) {
this.right = node;
} else {
// 递归的向右子树添加
this.right.add(node);
}
}
//当添加完一个结点后,如果: (右子树的高度-左子树的高度) > 1 , 左旋转
if(rightHeight() - leftHeight() > 1) {
//如果它的右子树的左子树的高度大于它的右子树的右子树的高度
if(right != null && right.leftHeight() > right.rightHeight()) {
//先对右子结点进行右旋转
right.rightRotate();
//然后在对当前结点进行左旋转
leftRotate(); //左旋转..
} else {
//直接进行左旋转即可
leftRotate();
}
return ; //必须要!!!
}
//当添加完一个结点后,如果 (左子树的高度 - 右子树的高度) > 1, 右旋转
if(leftHeight() - rightHeight() > 1) {
//如果它的左子树的右子树高度大于它的左子树的高度
if(left != null && left.rightHeight() > left.leftHeight()) {
//先对当前结点的左结点(左子树)->左旋转
left.leftRotate();
//再对当前结点进行右旋转
} //直接进行右旋转即可
rightRotate();
}
}
// 中序遍历
public void infixOrder() {
if (this.left != null) {
this.left.infixOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.infixOrder();
}
}
2.定义变量并初始化
private Node root;
public Node getRoot() {
return root;
}
3.查找要删除的结点
public Node search(int value) {
if (root == null) {
return null;
} else {
return root.search(value);
}
}
4.查找父结点
public Node searchParent(int value) {
if (root == null) {
return null;
} else {
return root.searchParent(value);
}
}
5.返回的 以node 为根结点的二叉排序树的最小结点的值,删除node 为根结点的二叉排序树的最小结点
public int delRightTreeMin(Node node) {
Node target = node;
// 循环的查找左子节点,就会找到最小值
while (target.left != null) {
target = target.left;
}
// 这时 target就指向了最小结点
// 删除最小结点
delNode(target.value);
return target.value;
}
6.删除结点
public void delNode(int value) {
if (root == null) {
return;
} else {
// 1.需求先去找到要删除的结点 targetNode
Node targetNode = search(value);
// 如果没有找到要删除的结点
if (targetNode == null) {
return;
}
// 如果我们发现当前这颗二叉排序树只有一个结点
if (root.left == null && root.right == null) {
root = null;
return;
}
// 去找到targetNode的父结点
Node parent = searchParent(value);
// 如果要删除的结点是叶子结点
if (targetNode.left == null && targetNode.right == null) {
// 判断targetNode 是父结点的左子结点,还是右子结点
if (parent.left != null && parent.left.value == value) { // 是左子结点
parent.left = null;
} else if (parent.right != null && parent.right.value == value) {// 是由子结点
parent.right = null;
}
} else if (targetNode.left != null && targetNode.right != null) { // 删除有两颗子树的节点
int minVal = delRightTreeMin(targetNode.right);
targetNode.value = minVal;
} else { // 删除只有一颗子树的结点
// 如果要删除的结点有左子结点
if (targetNode.left != null) {
if (parent != null) {
// 如果 targetNode 是 parent 的左子结点
if (parent.left.value == value) {
parent.left = targetNode.left;
} else { // targetNode 是 parent 的右子结点
parent.right = targetNode.left;
}
} else {
root = targetNode.left;
}
} else { // 如果要删除的结点有右子结点
if (parent != null) {
// 如果 targetNode 是 parent 的左子结点
if (parent.left.value == value) {
parent.left = targetNode.right;
} else { // 如果 targetNode 是 parent 的右子结点
parent.right = targetNode.right;
}
} else {
root = targetNode.right;
}
}
}
}
}
7.添加结点的方法
public void add(Node node) {
if (root == null) {
root = node;// 如果root为空则直接让root指向node
} else {
root.add(node);
}
}
8.中序遍历
public void infixOrder() {
if (root != null) {
root.infixOrder();
} else {
System.out.println("二叉排序树为空,不能遍历");
}
}
9.测试
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 };
//创建一个 AVLTree对象
AVLTree avlTree = new AVLTree();
//添加结点
for(int i=0; i < arr.length; i++) {
avlTree.add(new Node(arr[i]));
}
//遍历
System.out.println("中序遍历");
avlTree.infixOrder();
System.out.println("在平衡处理~~");
System.out.println("树的高度=" + avlTree.getRoot().height()); //3
System.out.println("树的左子树高度=" + avlTree.getRoot().leftHeight()); // 2
System.out.println("树的右子树高度=" + avlTree.getRoot().rightHeight()); // 2
System.out.println("当前的根结点=" + avlTree.getRoot());//8
}
34.AVL树的更多相关文章
- 二叉树之AVL树的平衡实现(递归与非递归)
这篇文章用来复习AVL的平衡操作,分别会介绍其旋转操作的递归与非递归实现,但是最终带有插入示例的版本会以递归呈现. 下面这张图绘制了需要旋转操作的8种情况.(我要给做这张图的兄弟一个赞)后面会给出这八 ...
- AVL树的算法思路整理
http://www.cnblogs.com/heqile/archive/2011/11/28/2265713.html 看完了<数据结构与算法分析(C++描述)>的4.4节AVL树,做 ...
- 数据结构与算法(九):AVL树详细讲解
数据结构与算法(一):基础简介 数据结构与算法(二):基于数组的实现ArrayList源码彻底分析 数据结构与算法(三):基于链表的实现LinkedList源码彻底分析 数据结构与算法(四):基于哈希 ...
- 二叉查找树(BST)、平衡二叉树(AVL树)(只有插入说明)
二叉查找树(BST).平衡二叉树(AVL树)(只有插入说明) 二叉查找树(BST) 特殊的二叉树,又称为排序二叉树.二叉搜索树.二叉排序树. 二叉查找树实际上是数据域有序的二叉树,即对树上的每个结点, ...
- AVL树原理及实现 +B树
1. AVL定义 AVL树是一种改进版的搜索二叉树.对于一般的搜索二叉树而言,如果数据恰好是按照从小到大的顺序或者从大到小的顺序插入的,那么搜索二叉树就对退化成链表,这个时候查找,插入和删除的时间都会 ...
- 自已动手作图搞清楚AVL树
@ 目录 一.背景 二.平衡二分搜索树---AVL树 2.1 AVL树的基本概念 结点 高度 平衡因子 2.2 AVL树的验证 三.旋转操作 3.1 L L--需要通过右旋操作 3.2 R R--需要 ...
- AVL树的插入和删除
一.AVL 树 在计算机科学中,AVL树是最早被发明的自平衡二叉查找树.在AVL树中,任一节点对应的两棵子树的最大高度差为 1,因此它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下的时间复杂度 ...
- 算法与数据结构(十一) 平衡二叉树(AVL树)
今天的博客是在上一篇博客的基础上进行的延伸.上一篇博客我们主要聊了二叉排序树,详情请戳<二叉排序树的查找.插入与删除>.本篇博客我们就在二叉排序树的基础上来聊聊平衡二叉树,也叫AVL树,A ...
- AVL树原理及实现(C语言实现以及Java语言实现)
欢迎探讨,如有错误敬请指正 如需转载,请注明出处http://www.cnblogs.com/nullzx/ 1. AVL定义 AVL树是一种改进版的搜索二叉树.对于一般的搜索二叉树而言,如果数据恰好 ...
随机推荐
- jquery gantt 的使用
1.引入css与js文件 <link rel="stylesheet" href="css/style.css" /> <script src ...
- java源码——计算立体图形的表面积和体积
计算球,圆柱,圆锥的表面积和体积. 利用接口实现. 上代码. Contants.java 常量存储类 package com.fuxuemingzhu.solidgraphics.contants; ...
- 【LeetCode】157. Read N Characters Given Read4 解题报告(C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 直接调用 日期 题目地址:https://leetco ...
- Mysql数据库语言学习的路线
对于我们数据库的学习,不管是测试人员还是开发人员以及我们的DBA来说重点都是SQL:但是我们的SQL可以分多少类型,学习重点又是在哪里呢,本文仅仅针对测试人员来展开说明: SQL:structure ...
- Java初学者作业——编写Java程序,实现判断所输入字符的类型(数字、小写字母、大写字母或其他字符)
返回本章节 返回作业目录 需求说明: 编写Java程序,实现判断所输入字符的类型(数字.小写字母.大写字母或其他字符) 实现思路: 声明变量c,用于存储用户输入的字符. 通过Scanner接收用户输入 ...
- Java初学者作业——定义管理员类(Admin),管理员类中的属性包括:姓名、账号、密码、电话;方法包括:登录、显示自己的信息。
返回本章节 返回作业目录 需求说明: 定义管理员类(Admin),管理员类中的属性包括:姓名.账号.密码.电话:方法包括:登录.显示自己的信息. 实现思路: 分析类的属性及其变量类型. 分析类的方法及 ...
- Oracle之增、删、改、查
结构化查询语言 (Structured Query Language, SQL) SQL的组成: 数据操作语言(DML) 对数据进行查询.插入.删除和修改等操作,例如SELECT.INSERT.UPD ...
- [学习笔记] IT项目管理 - 挣值管理(EVM)
挣值管理(EVM) 挣值管理(Earned Value Management, EVM)是成本管理里面较为重点的知识点,但是也可以用来综合考察项目范围.进度和成本绩效,经常需要做相关计算. 基本概念P ...
- ANT 通配符使用说明
通配符说明 通配符 说明 ? 匹配任意一个字符 * 匹配零个.一个.多个字符 ** 匹配零个.一个.多个目录 使用示例 URL路径 说明 /app/p?ttern 匹配 /app/pattern 和 ...
- Linux中常用的监控性能的命令(sar、mpstat,vmstat, iostat,)详解
Linux中常用的监控性能的命令有: sar:能查看CPU的平均信息,还能查看指定CPU的信息.与mpstat相比,sar能查看CPU历史信息 mpstat:能查看所有CPU的平均信息,还能查看指定C ...