java - AVL 树实现 - 不存储高度
问题描述
我目前正处于 AVL 树插入实现的中间,并且在插入和回溯树时,我正在努力保持平衡因素。
实际上,我可以找到的每个 AVL 实现作为示例都使用节点的两个子树的高度来计算平衡因子,类似于
node.balance = node.right.height - node.left.height
如果您的 Node 类看起来像
class Node {
int value, height;
Node left, right;
}
尽管问题在于对于这个特定的实现,跟踪节点的高度是“违反规则的”,而我们只能跟踪平衡因子。所以 Node 类看起来像
class Node {
int value, balance;
Node left, right;
}
我知道保持节点的平衡因子在概念上类似于保持每个插入树的高度,但是对于我的一生,我无法弄清楚平衡因子应该针对特定的所有情况发生变化节点。
目前我已经设置了平衡因子,而是通过递归调用每个节点的高度函数(不是最佳的!)来确保我的旋转和一般插入是正确的。
node.balance = height(node.right) - height(node.left)
whereheight()
递归遍历树以找到到叶子的最长路径。
而且我已经验证了旋转逻辑确实是正确的,但是当我开始编写代码以通过 +-1 增量回溯树来保持余额时,代码立即变成意大利面条,因为我显然不了解节点的基本知识平衡因素。
如果您想查看该代码,我已将其发布在下面(它有点长)。并且下面的实现也是一个String AVL Tree,但是思路是一样的。
任何输入表示赞赏,谢谢!
class StringAVLNode {
private String item;
private int balance;
private StringAVLNode left, right;
// just one constructor, please
public StringAVLNode(String str) {
item = str;
balance = 0;
left = null; right = null;
}
public int getBalance () {
return balance;
}
public void setBalance ( int bal){
balance = bal;
}
public String getItem () {
return item;
}
public StringAVLNode getLeft () {
return left;
}
public void setLeft (StringAVLNode pt){
left = pt;
}
public StringAVLNode getRight () {
return right;
}
public void setRight (StringAVLNode pt){
right = pt;
}
public void insert(String str) {
root = insert(str, root);
}
private StringAVLNode insert(String str, StringAVLNode t) {
// Base case - Just insert the node
if (t == null)
t = new StringAVLNode(str);
else {
int balance, leftChildBalance, rightChildBalance;
leftChildBalance = t.getLeft() != null ? t.getLeft().getBalance() : -99;
rightChildBalance = t.getRight() != null ? t.getRight().getBalance() : -99;
// Perform string comparisons to determine left/right insert
int compareResult = str.compareToIgnoreCase(t.getItem());
if (compareResult < 0) {
t.setLeft(insert(str, t.getLeft()));
if (t.getRight() == null)
t.setBalance(t.getBalance()-1);
else if (leftChildBalance == 0 && t.getLeft().getBalance() != 0)
t.setBalance(t.getBalance()-1);
else if (leftChildBalance == -99 && t.getLeft() != null)
t.setBalance(t.getBalance()-1);
}
else if (compareResult > 0) {
t.setRight(insert(str, t.getRight()));
if (t.getLeft() == null)
t.setBalance(t.getBalance()+1);
else if (rightChildBalance == 0 && t.getRight().getBalance() != 0)
t.setBalance(t.getBalance()+1);
else if (rightChildBalance == -99 && t.getRight() != null)
t.setBalance(t.getBalance()+1);
}
balance = t.getBalance();
// Verbosify booleans
boolean rightImbalance = balance > 1; boolean leftImbalance = balance < -1;
// Imbalance tree situation calls balanceTrees() to handle the rotation logic
// ( Keeps insert() succinct )
if (rightImbalance || leftImbalance)
t = balanceTrees(balance, t);
}
return t;
}
// Rotation Handler
private StringAVLNode balanceTrees(int balance, StringAVLNode t) {
// Verbosify boolean values
boolean rightHeavy = balance > 1; boolean leftHeavy = balance < -1;
boolean requiresDoubleLeft = t.getRight() != null && t.getRight().getBalance() <= -1;
boolean requiresDoubleRight = t.getLeft() != null && t.getLeft().getBalance() >= 1;
if (rightHeavy) {
/** Do double left rotation by right rotating the right child subtree, then
* rotate left
*/
if (requiresDoubleLeft) {
t.setRight(rotateRight(t.getRight()));
t.getRight().setBalance(0);
t = rotateLeft(t);
t.setBalance(0);
}
else {
t = rotateLeft(t);
t.setBalance(0);
if (t.getLeft() != null) t.getLeft().setBalance(0);
if (t.getRight() != null) t.getRight().setBalance(0);
}
}
/** Do double right rotation by left rotating the left child subtree, then
* rotate right
*/
else if (leftHeavy) {
if (requiresDoubleRight) {
t.setLeft(rotateLeft(t.getLeft()));
t.getLeft().setBalance(0);
t = rotateRight(t);
t.setBalance(0);
}
else {
t = rotateRight(t);
t.setBalance(0);
if (t.getLeft() != null) t.getLeft().setBalance(0);
if (t.getRight() != null) t.getRight().setBalance(0);
}
}
if (t.getLeft() != null) {
if (t.getLeft().getRight() != null && t.getLeft().getLeft() == null)
t.getLeft().setBalance(1);
else if (t.getLeft().getLeft() != null && t.getLeft().getRight() == null)
t.getLeft().setBalance(-1);
else if ((t.getLeft().getLeft() != null && t.getLeft().getRight() != null)
|| (t.getLeft().getLeft() == null && t.getLeft().getRight() == null))
t.getLeft().setBalance(0);
}
if (t.getRight() != null) {
if (t.getRight().getRight() != null && t.getRight().getLeft() == null)
t.getRight().setBalance(1);
else if (t.getRight().getLeft() != null && t.getRight().getRight() == null)
t.getRight().setBalance(-1);
else if ((t.getRight().getLeft() != null && t.getRight().getRight() != null)
|| (t.getRight().getLeft() == null && t.getRight().getRight() == null))
t.getRight().setBalance(0);
}
return t;
}
}
解决方案
查看我在 Java 中编写的 AVL 树:
https://debugnotes.wordpress.com/2015/01/07/implementing-an-avl-tree-in-java-part-2
您的实现似乎不包括任何类型的基于堆栈的元素(递归或基于数组)来跟踪您在树中的深度。这是能够导航自平衡树数据结构的关键部分 - 能够向下搜索,找到目标节点并对其执行某些操作,然后向后追踪到它开始导航的树的根节点,操作当你向后工作时它。使用递归是一种方法(即使用程序堆栈),或者您需要实现自己的堆栈(例如使用队列或 LinkedList),但除非您的代码具有记录其所在位置的内存结构,否则不幸的是它总是会丢失。
推荐阅读
- java - 有没有办法在 Android Studio 中通过 Ajax 发送多种类型的 HashMap 参数?
- python - 我怎样才能做一个循环?
- azure-devops - devops azure vsts biild下的脚本容器问题
- validation - 我正在尝试在 Vue.js 和 Php 中构建一个联系表单,但是我遇到了错误,我不太确定如何解决这个问题
- vb.net - 如何将鼠标位置转换为位置?
- python - 为什么我的原始二维数组在以下代码中被修改
- python - 如何使用 AWS Cognito 检索正确的凭证以访问 boto3 客户端上的 AWS SecretsManger - 身份池
- python - RPLY 解析器返回 ValueError
- python - 如何在滚动数据的子集上应用滚动聚合函数?
- javascript - 用 Java 下载带有 JavaScript Cookie 的文件