IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Java数据结构——平衡二叉树 -> 正文阅读

[数据结构与算法]Java数据结构——平衡二叉树

平衡二叉树:

在二叉排序树的创建中,若创建序列不恰当,可能会导致二叉排序树高度过大,甚至变成单链表

如序列1 2 3 4 5 6,显然每次插入新节点,都在右子树上插入,形成一个单链表

此时对该树的查询效率降低,没有发挥出二叉排序树的优势,为了解决这一问题,引入平衡二叉树

平衡二叉树满足以下两个条件

(1)是二叉排序树

(2)左右子树均为平衡二叉树,且高度差不大于1

空树也为平衡二叉树,且一般将某个节点的左右子树高度差称为该节点的平衡因子,记作BF

实现思路:

对于二叉树的失衡情况,可以简化为以下四种

LL型:

RR型:

LR型:

RL型:

这四种情况均需利用到二叉树的左旋和右旋进行处理,下面先介绍左旋和右旋

左旋:

如图可以看出,左旋是将整棵树向左边(逆时针)进行旋转,其中,根节点右子树的左子树,在旋转后变成根节点左子树的右子树,具体算法如下:

(1)以当前节点的值创建新节点

(2)把新的节点的左子树设置为当前节点的左子树

(3)把新的节点的右子树设置为当前节点右子树的左子树

(4)把当前节点的值修改为右孩子的值

(5)把当前节点的右子树设置为右孩子的右子树

(6)把当前节点的左孩子设置为新节点

经过这样的操作后,原先二叉树中会有一个节点(在本例中为3)会由于没有任何指针指向它,被Java的GC自动回收

注意的是,左旋和右旋是针对某个节点进行的,也就是说,算法中的当前节点即为进行左旋和右旋的节点,如本例中当前节点为根节点,此时根节点的平衡因子的绝对值大于1

右旋:

与左旋正好相反,具体算法如下:

(1)以当前节点的值创建新节点

(2)把新的节点的右子树设置为当前节点的右子树

(3)把新的节点的左子树设置为当前节点左子树的右子树

(4)把当前节点的值修改为左孩子的值

(5)把当前节点的左子树设置为左孩子的左子树

(6)把当前节点的右孩子设置为新节点

对于LL型和RR型,只需要从下往上找到第一个不平衡(平衡因子的绝对值大于1)的节点,进行左旋或右旋即可

对于LR型和RL型,需要进行两次旋转,具体如下:

LR型:先对左孩子进行左旋,再对根节点进行右旋

RL型:先对右孩子进行右旋,再对根节点进行左旋

为了判断节点是否平衡,还需设计获取子树高度的方法,按照树高等于左右子树的最大高度 + 1的思想,设计递归方法进行计算即可,较为简单

对于四种类型的判断,首先比较左右子树高度的关系,确定左旋还是右旋,然后进一步判断其左孩子(右孩子)的左右子树高度的关系,来确定是否需要两次旋转,四种情况如下:

LL型:左子树与右子树高度之差大于1,且左子树的左子树高度不小于左子树的右子树高度

LR型:左子树与右子树高度之差大于1,且左子树的左子树高度小于左子树的右子树高度

RR型:右子树与左子树高度之差大于1,且右子树的右子树高度不小于右子树的左子树高度

RL型:右子树与左子树高度之差大于1,且右子树的右子树高度小于右子树的左子树高度

在添加节点和删除节点中,都通过递归,实现了从下往上的平衡处理

代码实现:

节点类AVLTreeNode:

public class AVLTreeNode {
	public int data;
	public AVLTreeNode left;
	public AVLTreeNode right;

	public AVLTreeNode(int data) {
		this.data = data;
	}

	//返回左子树高度
	public int getLeftHeight() {
		if (left == null) {
			return 0;
		}
		return left.getHeight();
	}

	//返回右子树高度
	public int getRightHeight() {
		if (right == null) {
			return 0;
		}
		return right.getHeight();
	}

	//返回当前节点为根节点的树的高度
	public int getHeight() {
		return Math.max(left == null ? 0 : left.getHeight(), right == null ? 0 : right.getHeight()) + 1;
	}

	//左旋转
	public void leftRotate() {
		AVLTreeNode newNode = new AVLTreeNode(data);
		newNode.left = left;
		newNode.right = right.left;
		data = right.data;
		right = right.right;
		left = newNode;
	}

	//右旋转
	public void rightRotate() {
		AVLTreeNode newNode = new AVLTreeNode(data);
		newNode.right = right;
		newNode.left = left.right;
		data = left.data;
		left = left.left;
		right = newNode;
	}

	//添加节点
	public void add(AVLTreeNode node) {
		if (node == null) {
			return;
		}
		if (node.data < this.data) {
			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 (getRightHeight() - getLeftHeight() > 1) {
			if (right.getLeftHeight() > right.getRightHeight()) {
				right.rightRotate();
			}
			leftRotate();

		} else if (getLeftHeight() - getRightHeight() > 1) {
			if (left.getRightHeight() > left.getLeftHeight()) {
				left.leftRotate();
			}
			rightRotate();
		}
	}

	//查找待删除的节点
	public AVLTreeNode search(int data) {
		if (data == this.data) {
			return this;
		} else if (data < this.data) {
			if (this.left == null) {
				return null;
			}
			return this.left.search(data);
		} else {
			if (this.right == null) {
				return null;
			}
			return this.right.search(data);
		}
	}

	//查找待删除节点的父节点
	public AVLTreeNode searchParent(int data) {
		if (this.left != null && this.left.data == data || this.right != null && this.right.data == data) {
			return this;
		} else {
			if (this.left != null && data < this.data) {
				return this.left.searchParent(data);
			} else if (this.right != null && data >= this.data) {
				return this.right.searchParent(data);
			} else {
				return null;
			}
		}
	}

	//删除节点后平衡二叉树递归方法
	public void balanceAfterDelete(AVLTreeNode parentNode) {
		if (parentNode.data < this.data) {
			this.left.balanceAfterDelete(parentNode);
		} else if (parentNode.data > this.data) {
			this.right.balanceAfterDelete(parentNode);
		}
		if (getRightHeight() - getLeftHeight() > 1) {
			if (right.getLeftHeight() > right.getRightHeight()) {
				right.rightRotate();
			}
			leftRotate();

		} else if (getLeftHeight() - getRightHeight() > 1) {
			if (left.getRightHeight() > left.getLeftHeight()) {
				left.leftRotate();
			}
			rightRotate();
		}
	}

	//中序遍历
	public void inOrder() {
		if (this.left != null) {
			this.left.inOrder();
		}
		System.out.println(this);
		if (this.right != null) {
			this.right.inOrder();
		}
	}

	@Override
	public String toString() {
		return "AVLTreeNode{" + "data=" + data + '}';
	}
}

平衡二叉树类AVLTree:

public class AVLTree {
	public AVLTreeNode root;

	//返回高度
	public int getHeight() {
		return root.getHeight();
	}

	//添加节点
	public void add(AVLTreeNode node) {
		if (root == null) {
			root = node;
		} else {
			root.add(node);
		}
	}

	//查找待删除的节点
	public AVLTreeNode search(int data) {
		if (root == null) {
			return null;
		} else {
			return root.search(data);
		}
	}

	//查找待删除节点的父节点
	public AVLTreeNode searchParent(int data) {
		if (root == null) {
			return null;
		} else {
			return root.searchParent(data);
		}
	}

	//查找以node为根节点的二叉排序树的最小节点的值
	public int deleteRightTreeMin(AVLTreeNode node) {
		AVLTreeNode targetNode = node;
		while (targetNode.left != null) {
			targetNode = targetNode.left;
		}
		delete(targetNode.data);
		return targetNode.data;
	}

	//删除节点
	public void delete(int data) {
		if (root == null) {
			return;
		} else {
			AVLTreeNode targetNode = search(data);
			if (targetNode == null) {
				return;
			}
			if (root.left == null && root.right == null) {
				root = null;
				return;
			}
			AVLTreeNode parentNode = searchParent(data);
			if (targetNode.left == null && targetNode.right == null) {
				if (parentNode.left != null && parentNode.left.data == data) {
					parentNode.left = null;
				} else if (parentNode.right != null && parentNode.right.data == data) {
					parentNode.right = null;
				}
			} else if (targetNode.left != null && targetNode.right != null) {
				targetNode.data = deleteRightTreeMin(targetNode.right);
			} else {
				if (targetNode.left != null) {
					if (parentNode != null) {
						if (parentNode.left.data == data) {
							parentNode.left = targetNode.left;
						} else {
							parentNode.right = targetNode.left;
						}
					} else {
						root = targetNode.left;
					}
				} else {
					if (parentNode != null) {
						if (parentNode.left.data == data) {
							parentNode.left = targetNode.right;
						} else {
							parentNode.right = targetNode.right;
						}
					} else {
						root = targetNode.right;
					}
				}
			}
			balanceAfterDelete(parentNode);
		}
	}

	//删除节点后平衡二叉树递归方法
	public void balanceAfterDelete(AVLTreeNode parentNode) {
		root.balanceAfterDelete(parentNode);
	}

	//中序遍历
	public void inOrder() {
		if (this.root != null) {
			this.root.inOrder();
		} else {
			System.out.println("二叉树为空");
		}
	}
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-09-05 11:17:27  更:2021-09-05 11:19:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/30 1:32:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码