package avltree;
/**
* 平衡二叉搜索树 AVL树
*
* 首先应当是一颗二叉排序树(二叉搜索树)
* 要求该树的左右两颗子树高度的绝对值之差不大于1
*
* 当增加或删除元素时通过左旋、右旋、双旋按操作使该树维持在平衡二叉树的状态
*
*/
public class AVLTreeDemo {
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 = new AVLTree();
for (int i = 0; i < arr.length; i++) {
avlTree.add(new Node(arr[i]));
}
System.out.println("中序遍历············");
avlTree.infixOrder();
System.out.println("二叉树高度为: " + avlTree.getRoot().getTreeHeight());
System.out.println("左子树高度为: " + avlTree.getRoot().getLeftHeight());
System.out.println("右子树高度为: " + avlTree.getRoot().getRightHeight());
System.out.println(avlTree.getRoot());//8
System.out.println(avlTree.getRoot().getLeft());//7
System.out.println(avlTree.getRoot().getLeft().getLeft());//6
System.out.println(avlTree.getRoot().getLeft().getRight());//null
System.out.println(avlTree.getRoot().getRight());//10
}
}
class AVLTree{
private Node root;
public Node getRoot() {
return root;
}
public void setRoot(Node root) {
this.root = root;
}
//删除结点
public void deleteNode(int value){
//若根节点为空则直接结束方法
if(root == null){
return;
}else{
//若根节点不为空,则通过search方法获取当前节点
Node targetNode = search(value);
//如果要删除的结点不存在,则方法直接结束
if(targetNode == null){
return;
}
//如果要删除的结点存在,则继续获取他的父节点
Node parent = searchParent(value);
//若targetNode != null 且该树只有一个根节点则目标节点只能是根节点
if(root.getLeft() == null && root.getRight() == null){
//若整棵树只有一个根节点,且要删除该节点,则直接将该节点置为空即可
root = null;
return;
}
//分情况讨论, 1、要删除的结点是叶子节点 2、要删除的结点是只有一个子树的结点 3、要删除的结点是由两个子树的结点
if(targetNode.getLeft() == null && targetNode.getRight() == null){
//若目标结点的左右子树均为空,则可能有两种情况 1、该节点是叶子节点 2、该树只有一个根节点,且该节点是要删除的目标结点
//上面已经排除了情况2 在情况一的状况下,叶子节点的父节点必定不可能为空
//若要删除叶子节点,则将该节点的所在的父节点的某一子树置为空
if(parent.getLeft() != null && parent.getLeft().getValue() == value){
//要删除的结点为父节点的左子结点
parent.setLeft(null);
}
if(parent.getRight() != null && parent.getRight().getValue() == value){
//要删除的结点为父节点的右子结点
parent.setRight(null);
}
}else if(targetNode.getLeft() != null && targetNode.getRight() != null){
//若待删除结点的左右子树均不为空
//则不需要考虑待删除的结点是否为根节点,也不用考虑parentNode是否为空
//要删除拥有两个子树的结点则需要找到其左子树的最大值或右子树的最小值来替换现节点的值
int min = getMinValue(targetNode.getRight());
targetNode.setValue(min);
}else{
//排除上面两种情况后余下的情况均为目标节点有单一子树的情况
//此种情况下需要考虑待删除结点是否为根节点 因为删除此种结点需要使用parent结点
//若目标结点的左子树不为空
if(targetNode.getLeft() != null){
//若要删除的结点为根节点,即parent == null 则直接将子节点替换根节点
if(parent == null){
root = targetNode.getLeft();
}else{
//若待删除结点为父节点的左子节点
if(parent.getLeft().getValue() == value){
parent.setLeft(targetNode.getLeft());
}else{
//若待删除结点为父节点的左子节点
parent.setRight(targetNode.getLeft());
}
}
}else{
//若目标结点的右子树不为空
if(parent == null){
root = targetNode.getRight();
}else{
//若待删除结点为父节点的左子节点
if(parent.getLeft().getValue() == value){
parent.setLeft(targetNode.getRight());
}else{
//若待删除结点为父节点的右子节点
parent.setRight(targetNode.getRight());
}
}
}
}
}
}
//查询某个结点左子树的最小值
public int getMinValue(Node node){
//找到最小值的结点,并将其引用给target
Node target = node;
while(target.getLeft() != null){
target = target.getLeft();
}
//删除该拥有最小值的结点
deleteNode(target.getValue());
//将最小值返回
return target.getValue();
}
//添加元素
public void add(Node node){
if(root == null){
this.root = node;
}else{
this.root.add(node);
}
}
//中序遍历
public void infixOrder(){
if(root != null){
this.root.infixOrder();
}else {
System.out.println("根节点为空,无法遍历");
}
}
//根据value值查找结点
public Node search(int value){
if(this.root == null){
return null;
}
return this.root.search(value);
}
//查找value值的父节点
public Node searchParent(int value){
if(this.root == null){
return null;
}
return this.root.searchParent(value);
}
}
class Node{
private int value;
private Node left;
private Node right;
public Node(int value){
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
//添加节点
public void add(Node node){
if(node == null){
return;
}
//判断结点值大小
if(node.value < this.value){
//若待插入结点的值比当前结点小,则向左子树插入
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);
}
}
//右子树高度大于左子树,需要左旋来平衡二叉树
if(this.getRightHeight() - this.getLeftHeight() > 1){
//双旋 当右子树的左子树高度大于右子树的右子树高度时 需要先通过右旋平衡右子树,在整体左旋平衡整颗二叉树
if(this.right != null && this.right.getLeftHeight() > this.right.getRightHeight()){
this.right.rightRotate();
this.leftRotate();
}else{
this.leftRotate();
}
return;
}
//左子树高度大于右子树,需要右旋来平衡二叉树
if(this.getLeftHeight() - this.getRightHeight() > 1){
if(this.left != null && this.left.getRightHeight() > this.left.getLeftHeight()){
this.left.leftRotate();
this.rightRotate();
}else{
this.rightRotate();
}
}
}
//查找当前值的父节点
public Node searchParent(int value){
if((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)){
return this;
}else{
if(this.value > value && this.left != null){
return this.left.searchParent(value);
}else if(this.value <= value && this.right != null){
return this.right.searchParent(value);
}else{
return null;
}
}
}
//查找结点
public Node search(int value){
if(this.value == value){
return this;
}else if(this.value > value){
if(this.left == null){
return null;
}
return this.left.search(value);
}else{
if(this.right == null){
return null;
}
return this.right.search(value);
}
}
//中序遍历 二叉排序树中序遍历后的结果为从小到大的有序数列
public void infixOrder(){
if(this.left != null){
this.left.infixOrder();
}
System.out.println(this);
if(this.right != null){
this.right.infixOrder();
}
}
//求某树的高度
public int getTreeHeight(){
return Math.max(left == null? 0 : left.getTreeHeight() , right == null? 0 : right.getTreeHeight()) + 1;
}
//左子树的高度
public int getLeftHeight(){
if(left == null){
return 0;
}
return left.getTreeHeight();
}
//右子树的高度
public int getRightHeight(){
if(right == null){
return 0;
}
return right.getTreeHeight();
}
//左旋转 当右子树比左子树高度差大于1时 采用左旋转来平衡二叉树
public void leftRotate(){
//1、创建新节点。使新节点的值等于根节点的值
Node node = new Node(this.value);
//2、使新节点的左子树指向原根节点的左子树
node.setLeft(this.getLeft());
//3、使新节点的右子树指向原根节点的右子树的左子树
node.setRight(this.getRight().getLeft());
//4、将原根节点的值设置为其右子节点的值
this.value = this.getRight().getValue();
//5、将原根节点的右子树设置为其右子树的右子树
this.setRight(this.getRight().getRight());
//6、将原根节点的左子节点指向新节点
this.setLeft(node);
}
//右旋转 当左子树比右子树的高度差大于1时 采用右旋转来平衡二叉树
public void rightRotate(){
//1、创建新节点 使新节点的值等于根节点的值
Node node = new Node(this.value);
//2、使新节点的右子树指向原根节点的右子树
node.setRight(this.getRight());
//3、使新节点的左子树指向原根节点的左子树的右子树
node.setLeft(this.getLeft().getRight());
//4、将原根节点的值设置为其右子节点的值
this.value = this.getLeft().getValue();
//5、将原根节点的右子树设置为其右子树的右子树
this.setLeft(this.getLeft().getLeft());
//6、将原根节点的左子节点指向新节点
this.setRight(node);
}
}
数据结构与算法练习---平衡二叉搜索树创建练习代码
最新推荐文章于 2024-07-24 19:35:30 发布