I am trying to balance a binary tree recursively by adding the tree data into an array in order, balance that data/ nodes (to minimize height) then insert the now balanced data back into the tree in the correct order. I am stuck on how to correctly do this all recursively. Here is the code if you scroll down you will see rebalance along with the methods I made to attempt to balance this tree:
package adts;
import java.util.Stack;
import interfaces.*;
import nodes.BSTNode;
public class BinarySearchTree<T extends Comparable<T>>
implements BSTInterface<T>
{
protected BSTNode<T> root; // reference to the root of this BST
boolean found; // used by remove
// for traversals
protected ArrayQueue<T> inOrderQ;
protected ArrayQueue<T> preOrderQ;
protected ArrayQueue<T> postOrderQ;
protected BinarySearchTree left = null;
protected BinarySearchTree right = null;
protected T[] rebalanceArray;
public BinarySearchTree() {
root = null;
}
public void add (T element) {
root = recAdd(element, root);
}
private BSTNode<T> recAdd(T element, BSTNode<T> tree) {
if (tree == null) {
tree = new BSTNode<T>(element);
}
else {
if (element.compareTo(tree.getData()) <= 0) {
tree.setLeft(recAdd(element, tree.getLeft())); // add to left subtree
}
else {
tree.setRight(recAdd(element, tree.getRight())); // add to right subtree
}
}
return tree;
}
public boolean remove (T element) {
root = recRemove(element, root);
return found;
}
private BSTNode<T> recRemove(T element, BSTNode<T> tree) {
if (tree == null) {
found = false;
}
else {
if (element.compareTo(tree.getData()) < 0) {
tree.setLeft(recRemove(element, tree.getLeft()));
}
else {
if (element.compareTo(tree.getData()) > 0) {
tree.setRight(recRemove(element, tree.getRight()));
}
else {
tree = removeNode(tree);
found = true;
}
}
}
return tree;
}
private BSTNode<T> removeNode(BSTNode<T> tree) {
T payload;
if (tree.getLeft() == null) {
return tree.getRight();
}
else {
if (tree.getRight() == null) {
return tree.getLeft();
}
else {
payload = getPredecessor(tree.getLeft());
tree.setData(payload);
tree.setLeft(recRemove(payload, tree.getLeft()));
return tree;
}
}
}
private T getPredecessor(BSTNode<T> tree) {
while (tree.getRight() != null) {
tree = tree.getRight();
}
return tree.getData();
}
public int size() {
return recSize(root);
}
private int recSize(BSTNode<T> tree) {
if (tree == null) {
return 0;
}
else {
return recSize(tree.getLeft()) + recSize(tree.getRight()) + 1;
}
}
// this implementation of a size operation demonstrates that
// it is possible to visit all the nodes of the tree without recursion
public int size2() {
int count = 0;
if (root != null) {
LLStack<BSTNode<T>> hold = new LLStack<BSTNode<T>>();
BSTNode<T> currNode;
hold.push(root);
while (!hold.isEmpty()) {
currNode = hold.peek();
hold.pop();
count++;
if (currNode.getLeft() != null) {
hold.push(currNode.getLeft());
}
if (currNode.getRight() != null) {
hold.push(currNode.getRight());
}
}
}
return count;
}
public boolean isEmpty() {
return (root == null);
}
public boolean contains (T element) {
return recContains(element, root);
}
private boolean recContains(T element, BSTNode<T> tree) {
if (tree == null) return false;
else
if (element.compareTo(tree.getData()) < 0)
return recContains(element, tree.getLeft()); // search left subtree
else
if (element.compareTo(tree.getData()) > 0)
return recContains(element, tree.getRight()); // search right subtree
else
return true; // element is found!
}
public T get(T element) {
return recGet(element, root);
}
private T recGet(T element, BSTNode<T> tree) {
if (tree == null)
return null;
else
if (element.compareTo(tree.getData()) < 0)
return recGet(element, tree.getLeft()); // get from left subtree
else
if (element.compareTo(tree.getData()) > 0)
return recGet(element, tree.getRight()); // get from right subtree
else
return tree.getData(); // element is found!
}
public void rebalance() {
T[] rebalanceArray = (T[]) new Comparable[size()];
// I want to return the newly constructed/ balanced tree here
}
private BinarySearchTree constructBST(T[] rebalanceArray , int high, int low) {
if (low > high)
return null;
int mid = (low+high)/2;
BinarySearchTree node = new BinarySearchTree();
node.left = constructBST(rebalanceArray, low, mid - 1);
node.right = constructBST(rebalanceArray, mid + 1, high);
return node;
}
private void storeNodes(BSTNode<T> root, BinarySearchTree nodes) {
if(root == null)
return;
storeNodes(root.getLeft(), nodes);
nodes.add(root); //add function not working
storeNodes(root.getRight(), nodes);
}
// populate inOrderQ with tree elements based on in-order traversal
private void inOrder(BSTNode<T> tree) {
if (tree != null) {
inOrder(tree.getLeft());
inOrderQ.enqueue(tree.getData());
inOrder(tree.getRight());
}
}
// populate preOrderQ with tree elements based on pre-order traversal
private void preOrder(BSTNode<T> tree) {
if (tree != null) {
preOrderQ.enqueue(tree.getData());
preOrder(tree.getLeft());
preOrder(tree.getRight());
}
}
// populate postOrderQ with tree elements based on post-order traversal
private void postOrder(BSTNode<T> tree) {
if (tree != null) {
postOrder(tree.getLeft());
postOrder(tree.getRight());
postOrderQ.enqueue(tree.getData());
}
}
public int reset(TraversalType orderType) {
// returns current number of nodes in the tree
int numNodes = size();
switch (orderType) {
case INORDER :
inOrderQ = new ArrayQueue<T>(numNodes);
inOrder(root);
break;
case PREORDER :
preOrderQ = new ArrayQueue<T>(numNodes);
preOrder(root);
break;
case POSTORDER :
postOrderQ = new ArrayQueue<T>(numNodes);
postOrder(root);
break;
}
return numNodes;
}
public T getNext (TraversalType orderType) {
switch (orderType) {
case INORDER : return inOrderQ.dequeue();
case PREORDER : return preOrderQ.dequeue();
case POSTORDER: return postOrderQ.dequeue();
default: return null;
}
}
}
Here is the rebalance specific code:
public void rebalance() {
T[] rebalanceArray = (T[]) new Comparable[size()];
// I want to return the newly constructed/ balanced tree here
}
private BinarySearchTree constructBST(T[] rebalanceArray , int high, int low) {
if (low > high)
return null;
int mid = (low+high)/2;
BinarySearchTree node = new BinarySearchTree();
node.left = constructBST(rebalanceArray, low, mid - 1);
node.right = constructBST(rebalanceArray, mid + 1, high);
return node;
}
private void storeNodes(BSTNode<T> root, BinarySearchTree nodes) {
if(root == null)
return;
storeNodes(root.getLeft(), nodes);
nodes.add(root); //add function not working
storeNodes(root.getRight(), nodes);
}
input/ test tree:
package apps;
import adts.BinarySearchTree;
public class TestRUn800 {
public static void main(String[] args) {
BinarySearchTree<Integer> testTree1 = new BinarySearchTree<>();
testTree1.add(1);
testTree1.add(2);
testTree1.add(3);
testTree1.add(4);
testTree1.add(5);
testTree1.add(6);
testTree1.add(7);
System.out.println(testTree1.treeHeight() + " tree height");
System.out.println(testTree1.reckonUnbalanced() + " reckonUnbalanced Value");
System.out.print(testTree1.rebalance()); // rebalance statement
}
}
I did a lot of searching around on the web and saw some solutions that had a method that built the tree and stored it's data before then returning it in the original (rebalance()) method. That is what I have here in storeNodes and buildBST but cannot get it running correctly without errors. The error I am running into for add is "The method add(Comparable) in the type BinarySearchTree is not applicable for the arguments (BSTNode)" (I also commented next to add in the code so one can see where it is). Also as seen in the tree I find tree height, the unbalanced height (reckonUnbalanced) then I want to rebalance it and get it's new height (I have not written that code in this example though just what I plan to do once I get the actual code working for rebalance).
I am implementing an AVL Tree of the following type AVLTree<K extends Comparable<K>,V> basically every node of the Tree is a Key-Value Node, at the moment i am struggling with a method that deletes all the V values. This is what i have:
private void deleteAll(AVLTreeNode<K,V> root, V value){
if(root == null) return;
deleteAll(root.left, value);
deleteAll(root.right, value);
if(root.getValue().equals(value)){
delete(root.getKey());
}
}
Super easy approach (maybe not optimized O(nlog(n)) if you have a better optimized solution feel free to write it) using a bottom-up recursion.
The delete method:
public void delete(K key){
super.setRoot(delete(super.getRoot(), key));
}
private AVLTreeNode<K,V> delete(AVLTreeNode<K,V> root, K key) {
if (root == null) {
return root;
} else if (root.getKey().compareTo(key) > 0) {
root.setLeft(delete(root.left, key));
} else if (root.getKey().compareTo(key) < 0) {
root.setRight(delete(root.right, key));
} else {
if (root.left == null || root.right == null) {
root = (root.left == null) ? root.right : root.left;
} else {
AVLTreeNode<K,V> mostLeftChild = AVLTreeUtils.minValueNode(root.right);
root.key = mostLeftChild.getKey();
root.value = mostLeftChild.getValue();
root.setRight(delete(root.right, root.key));
}
size--;
}
if (root != null) {
root = AVLTreeUtils.rebalance(root);
}
return root;
}
What happens is that it deletes correclty most of the values but sometimes it lefts some of the values it had to delete there, here is one example:
DELETING ALL VALUES: 2
BEFORE DELETION OF EVERY: 2
->2
->0
->1
->2
->1
->1
->1
->0
->2
->2
AFTER DELETION
->2
->0
->1
->2
->1
->1
->1
->0
Time used: 0 ms tree size: 8
-------------------------------------------
Does tree contain value: 2? true
EDIT:
Here is all the code of the class
package structure.tree.balanced;
import structure.array.Array;
import structure.node.Node;
import structure.tree.Tree;
public class AVLTree<K extends Comparable<K>,V> extends Tree<AVLTree.AVLTreeNode<K, V>> {
private static final long serialVersionUID = 5046115177325966348L;
public boolean containsValue(V value) {
return containsValue(((AVLTreeNode<K,V>) super.getRoot()), value);
}
private boolean containsValue(AVLTreeNode<K,V> root, V value){
if(root == null) return false;
else {
if(root.getValue().equals(value)) return true;
else return containsValue(root.getLeft(), value) || containsValue(root.getRight(), value);
}
}
public boolean containsKey(K key){
return containsKey(((AVLTreeNode<K,V>) super.getRoot()), key);
}
private boolean containsKey(AVLTreeNode<K,V> root, K key){
if(root == null) return false;
else {
if(root.getKey().compareTo(key) < 0) return containsKey(root.getRight(), key);
else if(root.getKey().compareTo(key) > 0) return containsKey(root.getLeft(), key);
else return true;
}
}
public void delete(K key){
super.setRoot(delete(super.getRoot(), key));
}
private AVLTreeNode<K,V> delete(AVLTreeNode<K,V> root, K key) {
if (root == null) {
return root;
} else if (root.getKey().compareTo(key) > 0) {
root.setLeft(delete(root.left, key));
} else if (root.getKey().compareTo(key) < 0) {
root.setRight(delete(root.right, key));
} else {
if (root.left == null || root.right == null) {
root = (root.left == null) ? root.right : root.left;
} else {
AVLTreeNode<K,V> mostLeftChild = minValueNode(root.right);
root.key = mostLeftChild.getKey();
root.value = mostLeftChild.getValue();
root.setRight(delete(root.right, root.key));
}
if(size > 0) size--;
}
if (root != null) {
root = rebalance(root);
}
return root;
}
public void deleteAll(V value){
deleteAll(super.getRoot(), value);
}
private void deleteAll(AVLTreeNode<K,V> root, V value){
if(root == null) return;
deleteAll(root.left, value);
deleteAll(root.right, value);
if(root.getValue().equals(value)) delete(root.getKey());
}
public V get(K key){
return get(super.getRoot(), key);
}
private V get(AVLTreeNode<K, V> root, K key) {
if(root.getKey().compareTo(key) == 0) return root.getValue();
else if(root.getKey().compareTo(key) > 0) return get(root.getLeft(), key);
else return get(root.getRight(), key);
}
private int getBalance(AVLTreeNode<K,V> n) {
return (n == null) ? 0 : height(n.getRight()) - height(n.getLeft());
}
private int height(AVLTreeNode<K,V> n) {
return n == null ? -1 : n.getHeight();
}
public void insert(K key, V value) {
size++;
super.setRoot(insert(super.getRoot(), key, value));
}
private AVLTreeNode<K,V> insert(AVLTreeNode<K,V> root, K key, V value){
if (root == null) {
return new AVLTreeNode<K,V>(key, value);
} else if (root.getKey().compareTo(key) > 0) {
root.setLeft(insert(root.getLeft(), key, value));
} else if (root.getKey().compareTo(key) < 0) {
root.setRight(insert(root.getRight(), key, value));
} else {
size--;
throw new RuntimeException("duplicate Key!");
}
return rebalance(root);
}
private AVLTreeNode<K,V> minValueNode(AVLTreeNode<K,V> root){
if(root == null) return null;
else {
if(root.getLeft() != null) return minValueNode(root.getLeft());
else return root;
}
}
private AVLTreeNode<K,V> rebalance(AVLTreeNode<K,V> z) {
updateHeight(z);
int balance = getBalance(z);
if (balance > 1) {
if (height(z.getRight().getRight()) > height(z.getRight().getLeft())) {
z = rotateLeft(z);
} else {
z.setRight(rotateRight(z.getRight()));
z = rotateLeft(z);
}
} else if (balance < -1) {
if (height(z.getLeft().getLeft()) > height(z.getLeft().getRight()))
z = rotateRight(z);
else {
z.setLeft(rotateLeft(z.getLeft()));
z = rotateRight(z);
}
}
return z;
}
public void replace(K key, V newValue) {
replace(super.getRoot(), key, newValue);
}
private void replace(AVLTreeNode<K, V> root, K key, V newValue) {
if(root != null){
if(root.getKey().compareTo(key) == 0) root.setValue(newValue);
else if(root.getKey().compareTo(key) > 0) replace(root.getLeft(), key, newValue);
else replace(root.getRight(), key, newValue);
}
}
public void replaceAll(V oldValue, V newValue){
replaceAll(super.getRoot(), oldValue, newValue);
}
private void replaceAll(AVLTreeNode<K, V> root, V oldValue, V newValue){
if(root == null) return;
if(root.getValue().equals(oldValue)) root.setValue(newValue);
replaceAll(root.left, oldValue, newValue);
replaceAll(root.right, oldValue, newValue);
}
private AVLTreeNode<K,V> rotateLeft(AVLTreeNode<K,V> y){
AVLTreeNode<K,V> x = y.getRight();
AVLTreeNode<K,V> z = x.getLeft();
x.setLeft(y);
y.setRight(z);
updateHeight(y);
updateHeight(x);
return x;
}
private AVLTreeNode<K,V> rotateRight(AVLTreeNode<K,V> y){
AVLTreeNode<K,V> x = y.getLeft();
AVLTreeNode<K,V> z = x.getRight();
x.setRight(y);
y.setLeft(z);
updateHeight(y);
updateHeight(x);
return x;
}
public Object[] toArray(){
return toArray(super.getRoot(), new Array<>(size()));
}
private Object[] toArray(AVLTreeNode<K,V> root, Array<AVLTreeNode<K,V>> arr){
if(root != null){
toArray(root.left, arr);
arr.insert(root);
toArray(root.right, arr);
}
return arr.toArray();
}
private void updateHeight(AVLTreeNode<K,V> root){
root.setHeight(1 + Math.max(height(root.getLeft()), height(root.getRight())));
}
public static class AVLTreeNode<K extends Comparable<K>, V> implements Node<V> {
private static final long serialVersionUID = -2099221188924293375L;
private AVLTreeNode<K,V> left, right;
private K key;
private V value;
private int height = 0;
public AVLTreeNode(K key, V value){
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
#SuppressWarnings("unused")
private void setKey(K key) {
this.key = key;
}
public AVLTreeNode<K,V> getLeft() {
return left;
}
protected void setLeft(AVLTreeNode<K,V> left) {
this.left = left;
}
public AVLTreeNode<K,V> getRight() {
return right;
}
protected void setRight(AVLTreeNode<K,V> right) {
this.right = right;
}
#Override
public String toString() {
return "{\"key\":"+key+",\"value\":"+getValue()+"}";
}
#Override
public V getValue() {
return value;
}
#Override
public void setValue(V value) {
this.value = value;
}
public int getHeight() {
return height;
}
protected void setHeight(int height) {
this.height = height;
}
public int getBalance() {
return (left != null ? left.getHeight() : 0) - (right != null ? right.getHeight() : 0);
}
}
}
The superclass is this:
package structure.tree;
import structure.Structure;
import structure.node.Node;
/**
* A Tree is a {#Structure} that uses {#Node} as value.
*/
public abstract class Tree<N extends Node<?>> {
private static final long serialVersionUID = -2524427972418434522L;
private N root;
public void clear() {
root = null;
size = 0;
}
public boolean isEmpty() {
return size == 0;
}
public N getRoot() {
return root;
}
protected void setRoot(N value) {
this.root = value;
}
}
This is the code i run that gives problems note that sometimes it works and sometimes it doesnt:
AVLTree<Integer,Integer> avlt = new AVLTree<Integer,Integer>();
Random r = new Random();
for(int i = 1; i <= SIZE; i++){
avlt.insert(i, r.nextInt(keySize));
}
int vdel = r.nextInt(keySize);
avlt.deleteAll(vdel);
assertEquals(false, avlt.containsValue(vdel));
The problem is that the tree structure changes whilst you are trying to find nodes to delete.
Consider this tree, from which we will be deleting the 'X's:
2->X
/ \
1->X 3->X
\
4->O
The delete operation starts at '2'. As the '2' node has a left hand branch, we descend to the '1' node. The '1' node has neither left nor right branches so there is no further descent.
Node '1' must be deleted.
2->X
\
3->X
\
4->O
And then the tree needs rebalancing.
3->X
/ \
2->X 4->O
This completes the processing of the '1' node, so the recursive search moves back to '2'.
The left hand branch has been processed. Due to the rebalancing, there is no longer a right hand branch to process. The '2' node itself needs deleting.
3->X
\
4->O
This completes the processign of node '2' so we return. This completes the algorithm.
Nodes '3' and '4' were never inspected because of the rebalancing.
How to fix this?
The easiest solution is to separate the search from the "delete and rebalance" operation. Create a collection of all the keys that map to the value to be deleted. Once all such keys are identified, delete those mappings one at a time. As the tree does not change shape during the search, the search will find all the relevant key-value pairs.
My own preference would be to create an Iterator over the tree nodes that supports remove. Such an Iterator would be useful for tasks beyond deleting matching values. For such an iterator one needs to be able to identify the next node from any specified node. This means a node has to know what its parent is.
To that end, I suggest amending your code as follows:
The node implementation tracks its parent and its key cannot be changed.
public static class AVLTreeNode<K extends Comparable<K>, V> implements Node<V> {
private AVLTreeNode<K,V> left, right, parent;
private final K key;
private V value;
private int height = 0;
public AVLTreeNode(K key, V value){
this.key = key;
this.value = value;
}
protected void setLeft(AVLTreeNode<K,V> left) {
this.left = left;
if( left!=null ) {
left.parent = this;
}
}
public AVLTreeNode<K,V> getNext() {
AVLTreeNode<K,V> next;
// If a right branch exists, the next node is the left-most node in the right branch.
if( right!=null ) {
next = right;
while( next.left!=null ) {
next = next.left;
}
return next;
}
// The closest parent of which this is a left-branch descendant is the next node
next = this;
while( next.parent!=null ) {
if( next.parent.left==next ) {
return next.parent;
}
next = next.parent;
}
// no next node
return null;
}
protected void setRight(AVLTreeNode<K,V> right) {
this.right = right;
if( right!=null ) {
right.parent = this;
}
}
... rest of class is unchanged
}
The tree's insert and delete method can move a node to the root of the tree, so they also have to set the node's parent to null if that happens.
public void insert(K key, V value) {
size++;
AVLTreeNode<K,V> newRoot = insert(super.getRoot(),key,value);
newRoot.parent = null;
super.setRoot(newRoot);
}
public void delete(K key){
AVLTreeNode<K,V> newRoot = delete(super.getRoot(), key);
if( newRoot!=null ) {
newRoot.parent = null;
}
super.setRoot(newRoot);
}
The delete method changed the key of a node, moving it out of sequence. To preserve the sequence it cannot do that.
private AVLTreeNode<K,V> delete(AVLTreeNode<K,V> root, K key) {
if (root == null) {
return root;
} else if (root.getKey().compareTo(key) > 0) {
root.setLeft(delete(root.left, key));
} else if (root.getKey().compareTo(key) < 0) {
root.setRight(delete(root.right, key));
} else {
if (root.left == null || root.right == null) {
root = (root.left == null) ? root.right : root.left;
} else {
// Promote the next node to replace root.
AVLTreeNode<K,V> mostLeftChild = minValueNode(root.right);
mostLeftChild.setRight(delete(root.right, mostLeftChild.key));
mostLeftChild.setLeft(root.left);
root = mostLeftChild;
}
if(size > 0) size--;
}
if (root != null) {
root = rebalance(root);
}
return root;
}
Then we can create an Iterator:
public static class AVLNodeIterator<K extends Comparable<K>,V> implements Iterator<AVLTreeNode<K,V>> {
AVLTree<K,V> tree;
AVLTreeNode<K,V> current;
AVLTreeNode<K,V> next;
public AVLNodeIterator(AVLTree<K,V> tree) {
this.tree = tree;
current = null;
next = tree.minValueNode(tree.getRoot());
}
#Override
public boolean hasNext() {
return next!=null;
}
#Override
public AVLTreeNode<K, V> next() {
if( next==null ) {
throw new NoSuchElementException();
}
current = next;
next = current.getNext();
return current;
}
#Override
public void remove() {
if( current==null ) {
throw new IllegalStateException();
}
tree.delete(current.getKey());
current=null;
}
}
and finally we can rewrite deleteAll to use the new iterator:
public void deleteAll(V value){
Iterator<AVLTreeNode<K,V>> iterator = new AVLNodeIterator<>(this);
while( iterator.hasNext()) {
AVLTreeNode<K,V> node = iterator.next();
if( node.getValue().equals(value) ) {
iterator.remove();
}
}
}
I am trying to implement a splay(Node x) method for a Binary Search Tree. I have the leftRotation(Node x) and rightRotation(Node x) methods implemented correctly (atleast, I think they are...), but when I try to implement them in a splay(Node x) method, it calls itself in an infinite loop. Now, I know why it's doing that, but can't seem to figure out how to fix it.
Here is the leftRotation(Node x) method:
public void leftRotation(Node<E> x) {
if (x.getRightChild() == null) {
return;
}
Node<E> y = x.getRightChild();
x.setRightChild(y.getLeftChild());
if (y.getLeftChild() != null) {
y.getLeftChild().setParent(x);
}
y.setParent(x.getParent());
if (x.getParent() == null) {
root = y;
} else {
if (x == x.getParent().getLeftChild()) {
x.getParent().setLeftChild(y);
} else {
x.getParent().setRightChild(y);
}
}
y.setLeftChild(x);
x.setParent(y);
}
Here's the rightRotation(Node x) method:
public void rightRotation(Node<E> x) {
if (x.getLeftChild() == null) {
return;
}
Node<E> y = x.getLeftChild();
x.setRightChild(y.getRightChild());
if (y.getRightChild() != null) {
y.getRightChild().setParent(x);
}
y.setParent(x.getParent());
if (x.getParent() == null) {
root = y;
} else {
if (x == x.getParent().getRightChild()) {
x.getParent().setRightChild(y);
} else {
x.getParent().setLeftChild(y);
}
}
x.setRightChild(x);
x.setParent(y);
}
And here's the splay(Node x) method:
public void splay(Node<E> x) {
while (x.getParent() != null) {
if (x.isLeftChild && x.getParent().isLeftChild) {
this.rightRotation(x.getParent());
this.rightRotation(x);
} else if (x.isRightChild && x.getParent().isRightChild) {
this.leftRotation(x.getParent());
this.leftRotation(x);
} else if (x.isLeftChild && x.getParent().isRightChild) {
this.rightRotation(x);
this.leftRotation(x);
} else if (x.isRightChild() && x.getParent().isLeftChild()) {
this.leftRotation(x);
this.rightRotation(x);
} else if (x.isLeftChild && x.getParent() == root) {
this.rightRotation(x);
} else if (x.isRightChild && x.getParent() == root) {
this.leftRotation(x);
}
}
}
Any ideas on how to fix the infinite loop? It seems to be something to do with it not breaking out of the while(x.getParent() != null) statement in the splay(Node x) method, but when I went through the code using the debugger, the properties of the node seemed to be changing, so I don't really know where it's going wrong?
setLeftChild(Node leftChild) method:
public void setLeftChild(Node<E> leftChild) {
this.leftChild = leftChild;
if (leftChild != null) {
leftChild.setIsRightChild(true);
leftChild.setParent(this);
}
}
Apart from all the mistakes/bad things I pointed out in your code, here is the biggest one, in rightRotation :
x.setRightChild(x);
This creates a cycle in your tree, hence the infinite loop. You should have unit tested your methods. One other major error in your code is that your if - else if instructions do not have an else so there might be cases where nothing happens during an iteration... hence the infinite loop. It's not the case here because you considered all the cases (actually, you considered even more, and the two last ones will never be executed because the four first cases cover every possible case) but as a general remark, this was really dangerous to code it that way.
Here is the code of my own implementation of all these methods, which I deem cleaner :
public class BinaryTree<T extends Comparable<T>> {
private Node<T> root;
public void rebalance(Node<T> node) {
while (!node.isRoot()) rotation(node.getParent(), node.getChildKind().opposite());
}
private void rotation(Node<T> node, Side side) {
if (node.getChild(side.opposite()) == null) return;
Node<T> sideChild = node.getChild(side.opposite());
node.setChild(sideChild.getChild(side), side.opposite());
if (node.getParent() == null) setRoot(sideChild);
else node.getParent().setChild(sideChild, node.getChildKind());
sideChild.setChild(node, side);
}
private void setRoot(Node<T> root) {
this.root = root;
if (root != null) root.setRoot();
}
private static enum Side {
LEFT, RIGHT;
public Side opposite() { return this == LEFT ? RIGHT : LEFT; }
}
private static class Node<T extends Comparable<T>> {
private T value;
private Node<T> left, right, parent;
public Node(T value) { this(value, null, null, null); }
public Node(T value, Node<T> left, Node<T> right, Node<T> parent) {
setValue (value );
setLeft (left );
setRight (right );
setParent(parent);
}
public Node<T> setLeft(Node<T> left) {
this.left = left;
if (left != null) left.parent = this;
return this;
}
public Node<T> setRight(Node<T> right) {
this.right = right;
if (right != null) right.parent = this;
return this;
}
public Node<T> setChild(Node<T> child, Side side) { return side == Side.LEFT ? setLeft(child) : setRight(child); }
public Node<T> setRoot() { return setParent(null); }
private Node<T> setParent(Node<T> parent) {
this.parent = parent;
return this;
}
public Node<T> setValue(T value) {
this.value = notNull(value);
return this;
}
public boolean isRoot() { return parent == null; }
public boolean isLeftChild () { return isRoot() || getParent().getValue().compareTo(getValue()) > 0; }
public boolean isRightChild() { return isRoot() || !isLeftChild() ; }
public Node<T> getChild(Side side) { return side == Side.LEFT ? getLeft() : getRight(); }
public Side getChildKind() {
Check.isFalse(isRoot(), "This method is not defined on root nodes");
return isLeftChild() ? Side.LEFT : Side.RIGHT;
}
public T getValue () { return value ; }
public Node<T> getLeft () { return left ; }
public Node<T> getRight () { return right ; }
public Node<T> getParent() { return parent; }
}
}
Note : my tree is not always optimally rebalanced. I did this out of my head but I will check at Wikipedia to see what they say, I probably did not apply the right algorithm, but it works pretty fine already expect in pathologic cases.
My problem is in the add method. I think I know what I want it to do but I can't figure out what type of loop I should use to look through the list. As you can see I started to make a if else loop but I couldn't figure out what I should use as the counter. I'm pretty sure I have the right logic in dealing with the add but I feel like I'm not quite there yet. I was thinking of using compareTo in some fashion.
import java.util.*;
public class OrderedLinkedList<E extends Comparable<E>>
{
private Node topNode;
private class Node
{
private E data;
private Node nextNode;
public Node(E data)
{
this.data = data;
nextNode = null;
}
}
public OrderedLinkedList()
{
topNode = null;
}
public boolean empty()
{
if(topNode == null)
return true;
return false;
}
public String toString()
{
String myString = "";
Node nextNode = topNode;
while(nextNode != null)
{
myString = topNode + " -> " + nextNode;
nextNode = topNode.nextNode;
}
return myString;
}
public void add(E data)
{
Node myNode = new Node(data);
Node priorNode = topNode;
Node currentNode = topNode;
if(___)
{
priorNode = currentNode;
currentNode = currentNode.nextNode;
}
else
{
priorNode.nextNode = myNode;
myNode.nextNode = currentNode;
}
}
}
Since you don't typically know the length of a linked list until you've walked down it, the usual thing would be to use a while loop (as you've done in your toString() method)
Perhaps using a doubly linked list would be more beneficial. Consider the following alterations to your class:
import java.util.*;
public class OrderedLinkedList<E extends Comparable<E>>
{
private Node head;
private Node tail;
private class Node
{
private E data;
private Node nextNode;
private Node prevNode;
public Node(E data)
{
this.data = data;
nextNode = null;
prevNode = null;
}
public void setNext(Node node)
{
this.nextNode = node;
}
public Node getNext()
{
return this.nextNode;
}
public void setPrev(Node node)
{
this.prevNode = node;
}
public Node getPrev()
{
return this.prevNode;
}
public E getData()
{
return this.data;
}
public int compareTo(Node that) {
if(this.getData() < that.getData())
{
return -1;
}
else if(this.getData() == that.getData()
{
return 0;
}
else
{
return 1;
}
}
}
public OrderedLinkedList()
{
head = new Node(null);
tail = new Node(null);
head.setNext(tail);
tail.setPrev(head);
}
public boolean empty()
{
if(head.getNext() == tail)
{
return true;
}
return false;
}
public void add(E data) {
Node tmp = new Node(data);
if(this.empty()) {
this.addNodeAfterNode(tmp, head);
} else {
Node that = head.getNext();
// this while loop iterates over the list until finding the correct
// spot to add the new node. The correct spot is considered to be
// when tmp's data is >= that's data, or the next node after 'that'
// is tail. In which case the node is added to the end of the list
while((tmp.compareTo(that) == -1) && (that.getNext() != tail)) {
that = that.getNext();
}
this.addNodeAfterNode(tmp, that);
}
}
private void addNodeAfterNode(Node addNode, Node afterNode)
{
addNode.setNext(afterNode.getNext());
afterNode.getNext().setPrev(addNode);
afterNode.setNext(addNode);
addNode.setPrev(afterNode);
}
}
I'm adding values from an ArrayList of Strings to a BST and I'm coming up with a null pointer error on my line "tree.add(s);" and after tracing my code I can't figure out why this is happening. Can someone please help:
public class BinaryTree {
public Node root;
public BinaryTree tree;
private static class Node {
Node left;
Node right;
String data;
Node(String s) {
left = null;
right = null;
data = s;
}
}
public BinaryTree plantTree(ArrayList<String> dict) {
Collections.shuffle(dict);
for (String s : dict) {
s.toUpperCase();
System.out.print(s);
tree.add(s);
}
System.out.print(tree);
System.out.println();
return tree;
}
/**
* Creates an empty binary tree
*/
public BinaryTree() {
root = null;
}
public boolean search(String data) {
return (search(root, data));
}
private boolean search(Node node, String data) {
if (node == null) {
return (false);
}
if (data == node.data) {
return (true);
} else if (data.compareTo(node.data) > 0) {
return (search(node.left, data));
} else {
return (search(node.right, data));
}
}
public void add(String data) {
root = add(root, data);
}
private Node add(Node node, String data) {
if (node == null) {
node = new Node(data);
} else {
if (data.compareTo(node.data) > 0) {
node.left = add(node.left, data);
} else {
node.right = add(node.right, data);
}
}
return (node);
}
}
You have to set the tree variable to something before using it. For example:
public BinaryTree plantTree(ArrayList<String> dict) {
tree = new BinaryTree(); // important!
Collections.shuffle(dict);
for (String s : dict) {
s.toUpperCase();
System.out.print(s);
tree.add(s);
}
System.out.print(tree);
System.out.println();
return tree;
}
Maybe tree should be a local variable of the method rather than an instance variable?