AVL Tree Implementation - Without Storing Height - java

I'm currently in the middle of an AVL Tree insert implementation, and I am struggling with maintaining the balance factors while inserting and backtracking up the tree.
Virtually every AVL implementation I can find as an example uses the height of a node's two sub-tree's to calculate the balance factor, something along the lines of
node.balance = node.right.height - node.left.height
And this is perfectly fine if your Node class looks something like
class Node {
int value, height;
Node left, right;
}
Though the problem is that for this particular implementation, it is 'against the rules' to keep track of the height of the node, and instead we can only keep track of the balance factor. So the Node class instead looks like
class Node {
int value, balance;
Node left, right;
}
I know that maintaining the balance factor of a node is conceptually similar to maintaining the height for each insert into the tree, but for the life of me I can't figure out all of the situations in which the balance factor should change for a particular node.
At the moment I've got setting the balance factor implemented by instead recursively calling the height function for each and every node ( not optimal! ) to make sure my rotations and general insertion is correct.
node.balance = height(node.right) - height(node.left)
Where height() recursively traverses the tree to find the longest path to a leaf.
And I have verified that the rotation logic is indeed correct, but when I start writing code to maintain the balances by +-1 increments backtracking up the tree, the code immediately turns into spaghetti, as I am clearly not understanding something fundamental about the node balance factor.
If you want to see that code, I've posted it below ( its a bit long ). And the below implementation is also a String AVL Tree, but the idea is the same.
Any input is appreciated, Thanks!
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;
}
}

Check out my AVL Tree in Java writeup at:
https://debugnotes.wordpress.com/2015/01/07/implementing-an-avl-tree-in-java-part-2
It appears that your implementation does not include any kind of stack based element (recursive or array based) to keep track of how deep you are in the tree. This is a key part of being able to navigate self-balancing tree data structures - being able to search downwards, find and do something with a target node, and then trace backwards to the root node of the tree where it started navigating from, manipulating it as you work your back way up. Using recursion is one way (i.e. using the program stack) or you need to implement your own stack (e.g. use a Queue or LinkedList) but unless your code has a memory structure recording where it's been, unfortunately it'll always get lost.

Related

Find distance between two nodes in binary tree

Many answers on the net for 'finding Least Common Ancestor in binary tree' and its supplementary question 'find distance between 2 nodes' have 4 issues:
Does not consider duplicates
Does not consider if input node is invalid/absent/not in tree
Use extra / aux storage
Not truncating the traversal although answer is obtained.
I coded this sample to overcome all handicaps. but since I did not find 'a single' answer in this direction, I would appreciate if my code has a significant disadvantage which I am missing. Maybe there is none. Additional eyeballs appreciated.
public int distance(int n1, int n2) {
LCAData lcaData = new LCAData(null, 0, 0);
int distance = foundDistance (root, lcaData, n1, n2, new HashSet<Integer>());
if (lcaData.lca != null) {
return distance;
} else {
throw new IllegalArgumentException("The tree does not contain either one or more of input data. ");
}
}
private static class LCAData {
TreeNode lca;
int count;
public LCAData(TreeNode parent, int count) {
this.lca = parent;
this.count = count;
}
}
private int foundDistance (TreeNode node, LCAData lcaData, int n1, int n2, Set<Integer> set) {
assert set != null;
if (node == null) {
return 0;
}
// when both were found
if (lcaData.count == 2) {
return 0;
}
// when only one of them is found
if ((node.item == n1 || node.item == n2) && lcaData.count == 1) {
// second element to be found is not a duplicate node of the tree.
if (!set.contains(node.item)) {
lcaData.count++;
return 1;
}
}
int foundInCurrent = 0;
// when nothing was found (count == 0), or a duplicate tree node was found (count == 1)
if (node.item == n1 || node.item == n2) {
if (!set.contains(node.item)) {
set.add(node.item);
lcaData.count++;
}
// replace the old found node with new found node, in case of duplicate. this makes distance the shortest.
foundInCurrent = 1;
}
int foundInLeft = foundDistance(node.left, lcaData, n1, n2, set);
int foundInRight = foundDistance(node.right, lcaData, n1, n2, set);
// second node was child of current, or both nodes were children of current
if (((foundInLeft > 0 && foundInRight > 0) ||
(foundInCurrent == 1 && foundInRight > 0) ||
(foundInCurrent == 1 && foundInLeft > 0)) &&
lcaData.lca == null) {
// least common ancestor has been obtained
lcaData.lca = node;
return foundInLeft + foundInRight;
}
// first node to match is the current node. none of its children are part of second node.
if (foundInCurrent == 1) {
return foundInCurrent;
}
// ancestor has been obtained, aka distance has been found. simply return the distance obtained
if (lcaData.lca != null) {
return foundInLeft + foundInRight;
}
// one of the children of current node was a possible match.
return (foundInLeft + foundInRight) > 0 ? (foundInLeft + foundInRight) + 1 : (foundInLeft + foundInRight);
}
The algorithm appears to be (without pulling it apart entirely) to exhaustively traverse the entire tree until a node is found where there is one node found on the left and one on the right. And creating an additional set as you go.
The problem here seems to be that your algorithm is very inefficient. That may fit your requirements, if this particular operation is almost never carried out. But normally you could do better.

How do I traverse a link list looking two nodes ahead?

I have a link list, and I want to be able to look two nodes ahead. I need to check if the first two nodes have integers, and if they do, and the third node says ADD, then I need to condense that information into one node and free the other two nodes.
I'm confused about what should go in my while loop. I check if the third node points to null, but somehow that's not giving me the right output. I don't know if I'm handling my node.next correctly either. Some of this is pseudocode now.
while(node1.next.next.next != NULL){
if((node1.data.isInteger() && (node2.data.isInteger()){
if(node3.data.equals('add')){
node1.data = node1.data + node2.data;
} else {
//ERROR
}
garbage_ptr1 = node2;
garbage_ptr2 = node3;
node1.next = node3.next;
free(garbage_ptr1);
free(garbage_ptr2);
node2.next = node1.next.next;
node3.next = node2.next.next;
} else {
node1.next = node1.next.next;
node2.next = node1.next.next;
node3.next = node2.next.next;
}
An approach that I find easier is to maintain a small array that acts as a window onto the list, and to look for matches on the array. The code also becomes a lot cleaner and simpler if you move your null checks into utility methods. By doing these things, the loop over the list only needs to check the last element of the window to terminate.
A sketch of this in Java:
/* small utility methods to avoid null checks everywhere */
public static Node getNext(Node n) { return n != null ? n.next : null; }
public static boolean isInteger(Node n) {
return (n != null) && (n.data != null) && (n.data instanceof Integer);
}
public static boolean isAdd(Node n) {
return (n != null) && (n.data != null) && n.data.equals("add");
}
/* checks for a match in the 3-node window */
public boolean isMatch(Node[] w) {
return isInteger(w[0]) && isInteger(w[1]) && isAdd(w[2]);
}
/* Loads the 3-node window with 'n' and the next two nodes on the list */
public void loadWindow(Node[] w, Node n) {
w[0] = n; w[1] = getNext(w[0]); w[2] = getNext(w[1]);
}
/* shifts the window down by one node */
public void shiftWindow(Node[] w) { loadWindow(w, w[1]); }
...
Node[] window = new Node[3];
loadWindow( window, node1 );
while (window[2] != null) {
if (isMatch(window)) {
window[0].data = stack[0].data + stack[1].data;
window[0].next = window[2].next;
loadWindow(window, window[0]); // reload the stack after eliminating two nodes
} else {
shiftWindow( window );
}
}

Red Black Tree Top-Down Deletion Algorithm

I am implementing a Red Black Tree with insert, search and delete functions in O (log n) time. Insert and search are working fine. However I am stuck on delete. I found this ppt slide on the internet which shows the algorithm of RBT deletion: http://www.slideshare.net/piotrszymanski/red-black-trees#btnNext on page 56 onwards. I know I am asking a bit too much but I have been stuck on this for over 2 weeks and I can't find the problem. The way I'm understanding Top-Down deletion that you have to rotate and recolor nodes accordingly until you find the predecessor of the node to be deleted. When you do find this node - which would be either a leaf or a node with one right child, replace node to be deleted data by the data of this node and delete this node like normal BST deletion, right?
This is the code I did, based on what I learnt from that slide. If anyone would be so kind to go over it, I would be more than grateful! Or at least if you think there's a better algorithm than what I'm using, please tell me!
public void delete(int element){
if (root == null){
System.out.println("Red Black Tree is Empty!");
} else {
Node X = root;
parent = null;
grandParent = null;
sibling = null;
if (isLeaf(X)){
if (X.getElement() == element){
emptyRBT();
}
} else {
if (checkIfBlack(root.getLeftChild()) && checkIfBlack(root.getRightChild())){
root.setIsBlack(false);
if (X.getElement() > element && X.getLeftChild() != null){
X = moveLeft(X);
} else if (X.getElement() < element && X.getRightChild() != null){
X = moveRight(X);
}
Step2(X, element);
} else {
Step2B(X, element);
}
}
}
root.setIsBlack(true);
}
public void Step2(Node X, int element)
{
int dir = -1;
while (!isLeaf(X)){
if (predecessor == null){ // still didn't find Node to delete
if (X.getElement() > element && X.getLeftChild() != null){
X = moveLeft(X);
dir = 0;
} else if (X.getElement() < element && X.getRightChild() != null){
X = moveRight(X);
dir = 1;
} else if (X.getElement() == element){
toDelete = X;
predecessor = inorderPredecessor(X.getRightChild());
X = moveRight(X);
}
} else { // if node to delete is already found and X is equal to right node of to delete
// move always to the left until you find predecessor
if (X != predecessor){
X = moveLeft(X);
dir = 0;
}
}
if (!isLeaf(X)){
if (!hasOneNullNode(X)){
if (checkIfBlack(X.getLeftChild()) && checkIfBlack(X.getRightChild())){
Step2A(X, element, dir);
} else {
Step2B(X, element);
}
}
}
}
removeNode(X);
if (predecessor != null){
toDelete.setElement(X.getElement());
}
}
public Node Step2A(Node X, int element, int dir) {
if (checkIfBlack(sibling.getLeftChild()) && checkIfBlack(sibling.getRightChild())) {
X = Step2A1(X);
} else if ((checkIfBlack(sibling.getLeftChild()) == false) && checkIfBlack(sibling.getRightChild())) {
X = Step2A2(X);
} else if ((checkIfBlack(sibling.getLeftChild()) && (checkIfBlack(sibling.getRightChild()) == false))) {
X = Step2A3(X);
} else if ((checkIfBlack(sibling.getLeftChild()) == false) && (checkIfBlack(sibling.getRightChild()) == false)) {
X = Step2A3(X);
}
return X;
}
public Node Step2A1(Node X) {
X.setIsBlack(!X.IsBlack());
parent.setIsBlack(!parent.IsBlack());
sibling.setIsBlack(!sibling.IsBlack());
return X;
}
public Node Step2A2(Node X) {
if (parent.getLeftChild() == sibling){
LeftRightRotation(sibling.getLeftChild(), sibling, parent);
} else RightLeftRotation(sibling.getRightChild(), sibling, parent);
X.setIsBlack(!X.IsBlack());
parent.setIsBlack(!parent.IsBlack());
return X;
}
public Node Step2A3(Node X) {
if (parent.getLeftChild() == sibling){
leftRotate(sibling);
} else if (parent.getRightChild() == sibling){
rightRotate(sibling);
}
X.setIsBlack(!X.IsBlack());
parent.setIsBlack(!parent.IsBlack());
sibling.setIsBlack(!sibling.IsBlack());
sibling.getRightChild().setIsBlack(!sibling.getRightChild().IsBlack());
return X;
}
public void Step2B(Node X, int element){
if (predecessor == null){
if (X.getElement() > element && X.getLeftChild() != null){
X = moveLeft(X);
} else if (X.getElement() < element && X.getRightChild() != null){
X = moveRight(X);
} else if (X.getElement() == element){
Step2(X, element);
}
} else {
if (X != predecessor)
X = moveLeft(X);
else Step2(X, element);
}
if (X.IsBlack()){
if (parent.getLeftChild() == sibling){
leftRotate(sibling);
} else if (parent.getRightChild() == sibling){
rightRotate(sibling);
}
parent.setIsBlack(!parent.IsBlack());
sibling.setIsBlack(!sibling.IsBlack());
Step2(X, element);
} else {
Step2B(X, element);
}
}
public void removeNode(Node X) {
if (isLeaf(X)) {
adjustParentPointer(null, X);
count--;
} else if (X.getLeftChild() != null && X.getRightChild() == null) {
adjustParentPointer(X.getLeftChild(), X);
count--;
} else if (X.getRightChild() != null && X.getLeftChild() == null) {
adjustParentPointer(X.getRightChild(), X);
count--;
}
}
public Node inorderPredecessor(Node node){
while (node.getLeftChild() != null){
node = node.getLeftChild();
}
return node;
}
public void adjustParentPointer(Node node, Node current) {
if (parent != null) {
if (parent.getElement() < current.getElement()) {
parent.setRightChild(node);
} else if (parent.getElement() > current.getElement()) {
parent.setLeftChild(node);
}
} else {
root = node;
}
}
public boolean checkIfBlack(Node n){
if (n == null || n.IsBlack() == true){
return true;
} else return false;
}
public Node leftRotate(Node n)
{
parent.setLeftChild(n.getRightChild());
n.setRightChild(parent);
Node gp = grandParent;
if (gp != null){
if (gp.getElement() > n.getElement()){
gp.setLeftChild(n);
} else if (gp.getElement() < n.getElement()){
gp.setRightChild(n);
}
} else root = n;
return n;
}
public Node rightRotate(Node n)
{
parent.setRightChild(n.getLeftChild());
n.setLeftChild(parent);
Node gp = grandParent;
if (gp != null){
if (gp.getElement() > n.getElement()){
gp.setLeftChild(n);
} else if (gp.getElement() < n.getElement()){
gp.setRightChild(n);
}
} else root = n;
return n;
}
The node is being deleted, but the tree after deletion would be black violated, which is very wrong.
The eternally confuzzled blog has top-down implementations of both insert and delete for red-black trees. It also goes through case-by-case why it works. I won't replicate it here (it's rather lengthy).
I've used that blog as a reference for implementing red-black trees in both c++ and java. As I discussed in an earlier answer, I found the implementation to be faster than std::map's bottom-up implementation of red-black trees (whatever STL came with gcc at the time).
Here's an untested, direct translation of the code to Java. I would highly suggest you test it and morph it to match your style.
private final static int LEFT = 0;
private final static int RIGHT = 1;
private static class Node {
private Node left,right;
private boolean red;
...
// any non-zero argument returns right
Node link(int direction) {
return (direction == LEFT) ? this.left : this.right;
}
// any non-zero argument sets right
Node setLink(int direction, Node n) {
if (direction == LEFT) this.left = n;
else this.right = n;
return n;
}
}
boolean remove(int data) {
if ( this.root != null ) {
final Node head = new Node(-1, null, null); /* False tree root */
Node cur, parent, grandpa; /* Helpers */
Node found = null; /* Found item */
int dir = RIGHT;
/* Set up helpers */
cur = head;
grandpa = parent = null;
cur.setLink(RIGHT, this.root);
/* Search and push a red down */
while ( cur.link(dir) != null ) {
int last = dir;
/* Update helpers */
grandpa = parent, parent = cur;
cur = cur.link(dir);
dir = cur.data < data ? RIGHT : LEFT;
/* Save found node */
if ( cur.data == data )
found = cur;
/* Push the red node down */
if ( !is_red(cur) && !is_red(cur.link(dir)) ) {
if ( is_red(cur.link(~dir)) )
parent = parent.setLink(last, singleRotate(cur, dir));
else if ( !is_red(cur.link(~dir)) ) {
Node s = parent.link(~last);
if ( s != null ) {
if (!is_red(s.link(~last)) && !is_red(s.link(last))) {
/* Color flip */
parent.red = false;
s.red = true;
cur.red = true;
}
else {
int dir2 = grandpa.link(RIGHT) == parent ? RIGHT : LEFT;
if ( is_red(s.link(last)) )
grandpa.setLink(dir2, doubleRotate(parent, last));
else if ( is_red(s.link(~last)) )
grandpa.setLink(dir2, singleRotate(parent, last));
/* Ensure correct coloring */
cur.red = grandpa.link(dir2).red = true;
grandpa.link(dir2).link(LEFT).red = false;
grandpa.link(dir2).link(RIGHT).red = false;
}
}
}
}
}
/* Replace and remove if found */
if ( found != null ) {
found.data = cur.data;
parent.setLink(
parent.link(RIGHT) == cur ? RIGHT : LEFT,
cur.link(cur.link(LEFT) == null ? RIGHT : LEFT));
}
/* Update root and make it black */
this.root = head.link(RIGHT);
if ( this.root != null )
this.root.red = false;
}
return true;
}
quick link :
http://algs4.cs.princeton.edu/33balanced/RedBlackBST.java.html
--> Caution : the code on the site is relying on two jars. In the datastructures however the dependency might be minimal. Sometimes it's enough to comment out the main method (that only serves as a test client)
If not : the jars are downloadable on the same site.
If you are looking for two weeks and studying algoritms, chances are you know about
http://algs4.cs.princeton.edu/
the website that is accompanying the famous
Algorithms, by Robert Sedgewick and Kevin Wayne
book.
On this website, there is this implementation of a red black (balances) tree :
http://algs4.cs.princeton.edu/33balanced/RedBlackBST.java.html
I didnot look into it yet (I will later on this year) , but I fully trust it to be a working implementation of a RBTree.
Some sidenote that might be interesting for visitors of this topic:
MIT placed excellent courses concerning algoritms online. The one concerning rbtrees is
http://www.youtube.com/watch?v=iumaOUqoSCk

Recursively searching binary tree issue

I have written the below code for recursively searching binary tree .
Even though my system.out statement is getting executed , the return statement is not returning out of entire recursion and thus this method not returning true.
Can anyone suggest how can I return out of entire recursion.?
public static boolean isElementinTree(int num, BinaryTreeNode root)
{
if (root != null)
{
int rootVal = root.getData();
BinaryTreeNode left = root.getLeft();
BinaryTreeNode right = root.getRight();
if (left != null)
{
isElementinTree(num,left);
}
if (right != null)
{
isElementinTree(num,right);
}
if (num == rootVal)
{
System.out.println("------ MATCH -----");
return true;
}
}
return false;
}
This is the problem:
if (left != null)
{
isElementinTree(num,left);
}
if (right != null)
{
isElementinTree(num,right);
}
You're calling the method in those cases - but ignoring the result. I suspect you just want to change each of those to return immediately if it's found:
if (left != null && isElementinTree(num, left))
{
return true;
}
if (right != null && isElementinTree(num, right))
{
return true;
}
Or to make the whole thing more declarative, you can do it more simply:
public static boolean isElementinTree(int num, BinaryTreeNode root)
{
return root != null && (root.getData() == num ||
isElementInTree(num, root.getLeft()) ||
isElementInTree(num, root.getRight()));
}
It's fine to call isElementInTree with a null second argument, as you're already protecting against that with the first part.
What is wrong with a simple solution like this:
public static boolean isElementinTree(int num, BinaryTreeNode root)
{
return root != null && //The tree is non-null
(num == root.getData() || //We have num in this node OR
isElementInTree(num, root.getLeft()) || //We have num in left subtree OR
isElementInTree(num, root.getRight()) ); //We have num in right subtree
}
You need to check if the value is in one of the branches, and save that result.
Initialize a variable boolean found = false;.
When you do the recursive call, you need to do something like:
found = isElementinTree(num,left)
same thing for the right side.
At the end, instead of returning false, check if the value was found on a branch, simply return found;
Also, first check if the number you are looking for isn't on the Node itself, instead of searching each branch first. Simply switch the order of the if's.
If you do find the element you're looking for in the left or right subtrees you need to return this fact back up to the caller:
if (left != null)
{
if(isElementinTree(num,left)) return true;
}
if (right != null)
{
if(isElementinTree(num,right)) return true;
}
Only if you find it in none of the left tree, right tree and current node do you eventually fall through to the final return false.
Recursion solution:
boolean isElementinTree (int num, BinaryTreeNode root)
{
if(root == null)
return false;
if(root.value == num)
return true;
boolean n1 = isElementinTree(num,root.getLeft());
boolean n2 = isElementinTree(num,root.getRight());
return n1 ? n1 : n2;
}

Balancing check never terminates in Avl tree insertion

Writing an AVL Tree to hold generics for my data structures course; in my add() method, after actually inserting an element, I step back up through its ancestors checking their scores. For the first few additions it works, but (presumably at the point where balancing does need to be done), the loop back up the path fails to terminate. I've tried everything I can think of to make sure the root of the tree's parent isn't getting set to another node or anything like that. I'm calculating balance scores as right minus left, so positive means a tree is right-heavy and negative means left-heavy. Here's my add():
public void add(T e){
if (e == null)
return;
if (root == null) {
root = new TNode<T>(null, e);
return;
}
boolean added = false;
TNode<T> current = root;
while (current != null && added != true) { //insertion loop
if (current.compareTo(e) == 0)
return;
else if (current.compareTo(e) < 0) {
if (current.getRight() == null) {
current.setRight(new TNode<T>(current, e));
added = true;
}
else
current = current.getRight();
}
else if (current.compareTo(e) > 0) {
if (current.getLeft() == null) {
current.setLeft(new TNode<T>(current, e));
added = true;
}
else
current = current.getLeft();
}
}
if (useAVL == false)
return;
//balancing, checking up from added node to find where tree is unbalanced; currently loop does not terminate
//current is now parent of inserted node
while (current.hasParent()) {
if (current.getAvl() > 1) {
if (current.getRight().getAvl() > 0)
current = rotateLeft(current);
else if (current.getRight().getAvl() < 0) {
current.setRight(rotateRight(current.getRight()));
current = rotateLeft(current);
}
}
if (current.getAvl() < -1) {
if (current.getLeft().getAvl() < 0)
current = rotateRight(current);
else if (current.getLeft().getAvl() > 0) {
current.setLeft(rotateLeft(current.getLeft()));
current = rotateRight(current);
}
}
current = current.getParent();
}
root = current;
root.setParent(null);
}
And here's the right rotation method:
private TNode<T> rotateRight(TNode<T> old) {
TNode<T> oldRoot = old;
TNode<T> newRoot = old.getLeft();
TNode<T> rightChildNewRoot = newRoot.getRight(); //was right child of what will become root, becomes left child of old root
newRoot.setRight(oldRoot);
oldRoot.setParent(newRoot);
oldRoot.setLeft(rightChildNewRoot);
if (rightChildNewRoot != null)
rightChildNewRoot.setParent(oldRoot);
newRoot.setParent(oldRoot.getParent());
return newRoot;
}

Categories