I'm trying to implement a Dictionary using a Red-Black tree.
I've tested the insert method and it seems to work good, the RBtree seems to keep the correct shape and colors. The method that performs the binary tree node deletion seems to be correct, but I'm having huge problems on the deleteFixUp method called at the end of the deletion.
Would you like to help me figure out what I'm doing wrong? And, of course, if you have any suggestion to improve my code it would be very appreciated.
RBTreeWParentDictionary.java (Here I implemented the RedBlackTree)
package dictionary;
import java.util.Comparator;
public class RBTreeWParentDictionary<K, V> implements IDictionary<K, V> {
/**
* The root node of the RBTreeWParentDictionary
*/
public RBTreeWParentNode<K, V> root;
/**
* Object used to compare two T objects.
*/
private Comparator<K> comparator;
private int length;
/**
* Creates the dictionary based on red/black tree with null root
*
* #param comparator
* The comparator for keys
*/
public RBTreeWParentDictionary(Comparator<K> comparator) {
this.root = null;
this.comparator = comparator;
this.length = 0;
}
/**
* Checks if the tree is empty
*
* #return True if the tree is empty
*/
public boolean isEmpty() {
return this.root == null;
}
/**
* Returns the number of elements in the tree
*
* #return The number of elements in the tree
*/
public int length() {
return this.length;
}
/**
* Performs a left rotation on the tree node
*
* #param node
* The node on which rotate
*/
private void rotateLeft(RBTreeWParentNode<K, V> node) {
RBTreeWParentNode<K, V> y = node.getRight();
node.setRight(y.getLeft());
if (y.hasLeft()) {
y.getLeft().setParent(node);
}
y.setParent(node.getParent());
if (!node.hasParent()) { // = this.isEmpty()
this.root = y;
}
else {
if (node.equals(node.getParent().getLeft())) {
node.getParent().setLeft(y);
}
else {
node.getParent().setRight(y);
}
}
y.setLeft(node);
}
/**
* Performs a right rotation on the tree node
*
* #param node
* The node on which rotate
*/
private void rotateRight(RBTreeWParentNode<K, V> node) {
RBTreeWParentNode<K, V> y = node.getLeft();
node.setLeft(y.getRight());
if (y.hasRight()) {
y.getRight().setParent(node);
}
y.setParent(node.getParent());
if (!node.hasParent()) {
this.root = y;
}
else {
if (node.equals(node.getParent().getRight())) {
node.getParent().setRight(y);
}
else {
node.getParent().setLeft(y);
}
}
y.setRight(node);
}
/*
* Uses for first tests, now removed
*
* public void testRotateLeft() { this.rotateLeft(this.root); }
*
* public void testRotateRight() { this.rotateRight(this.root); }
*/
/**
* Performs all the needed work to the tree under the 3 main rules of R/BTree
*
* #param node
* The current node that needs to be checked
*/
private void treeFixUp(RBTreeWParentNode<K, V> node) {
RBTreeWParentNode<K, V> u;
if (!node.hasParent()) {
return;
}
while (node.getParent().isRed()) {
if (node.getParent().equals(node.getParent().getParent().getLeft())) {
u = node.getParent().getParent().getRight();
if (u != null && u.isRed()) {
node.getParent().setBlack();
u.setBlack();
node.getParent().getParent().setRed();
node = node.getParent().getParent();
}
else {
if (node.equals(node.getParent().getRight())) {
node = node.getParent();
rotateLeft(node);
}
node.getParent().setBlack();
node.getParent().getParent().setRed();
rotateRight(node.getParent().getParent());
}
}
else {
u = node.getParent().getParent().getLeft();
if (u != null && u.isRed()) {
node.getParent().setBlack();
u.setBlack();
node.getParent().getParent().setRed();
node = node.getParent().getParent();
}
else {
if (node.equals(node.getParent().getLeft())) {
node = node.getParent();
rotateRight(node);
}
node.getParent().setBlack();
node.getParent().getParent().setRed();
rotateLeft(node.getParent().getParent());
}
}
if (!node.hasParent()) {
node.setBlack();
break;
}
}
}
/**
* Inserts a node with give key/value
*
* #param key
* The key of the node to be inserted
* #param value
* The value of the node to be inserted
*/
#Override
public void insert(K key, V value) {
int res;
RBTreeWParentNode<K, V> insertedNode = new RBTreeWParentNode<K, V>(key,
value);
if (this.isEmpty()) {
this.root = insertedNode;
this.root.setBlack();
}
else {
RBTreeWParentNode<K, V> node = this.root;
while (node != null) {
res = comparator.compare(key, node.getKey());
if (res < 0) {
if (node.hasLeft()) {
node = node.getLeft();
}
else break;
}
else if (res > 0) {
if (node.hasRight()) {
node = node.getRight();
}
else break;
}
else { // duplicate key, overwriting
node.setValue(value);
return;
}
}
res = comparator.compare(key, node.getKey());
if (res < 0) {
node.setLeft(insertedNode);
}
else {
node.setRight(insertedNode);
}
treeFixUp(insertedNode);
this.length++;
}
}
#Override
public V get(K key) {
// TODO Auto-generated method stub
return null;
}
#Override
public void delete(K key) {
RBTreeWParentNode<K, V> node = root;
boolean oldColor;
int res;
while (node != null
&& (res = comparator.compare(key, node.getKey())) != 0) {
if (res < 0) node = node.getLeft();
else node = node.getRight();
}
if (node == null)
return;
oldColor = node.getColor();
// key found, work with children
if (!node.hasParent()) {//In root
root = null;
return;
}
else if(node.hasLeft() && !node.hasRight()) {//left child
node.getLeft().setParent(node.getParent());
node.getParent().setLeft(node.getLeft());
}
else if (!node.hasLeft() && node.hasRight()) {//right child
node.getRight().setParent(node.getParent());
node.getParent().setRight(node.getRight());
}
else if (node.hasLeft() && node.hasRight()) {//both children
RBTreeWParentNode<K, V> tmp = node;
node = min(tmp.getRight());
//fix parent node of node
node.setParent(tmp.getParent());
if (tmp.getParent().getLeft().equals(tmp)) {
node.getParent().setLeft(node);
}
else node.getParent().setRight(node);
node.setRight(deleteMin(tmp.getRight()));
node.setLeft(tmp.getLeft());
tmp = null;
}
else { // is a leaf
if (node.equals(node.getParent().getLeft()) ) {
node.getParent().setLeft(null);
}
else node.getParent().setRight(null);
}
if (oldColor == false) {
deleteFixUp(node);
}
}
private RBTreeWParentNode<K, V> deleteMin(
RBTreeWParentNode<K, V> node) {
if (node.getLeft() == null) {
return node.getRight();
}
node.setLeft(deleteMin(node.getLeft()));
return node;
}
private RBTreeWParentNode<K, V> min(RBTreeWParentNode<K, V> node) {
if (node.getLeft() == null) {
return node;
}
else return min(node.getLeft());
}
private void deleteFixUp(RBTreeWParentNode<K, V> node) {
while (!node.equals(this.root) && node.isBlack()) {
if (node.equals(node.getParent().getLeft())) {
if (node.getParent().hasRight()) {
RBTreeWParentNode<K, V> w = node.getParent().getRight();
if (w.isRed()) {
w.setBlack();
node.getParent().setRed();
rotateLeft(node.getParent());
w=node.getParent().getRight();
}
if (w.hasLeft() && w.hasRight() && w.getLeft().isBlack() && w.getRight().isBlack()) {
w.setRed();
node = node.getParent();
}
else {
if (w.hasRight() && w.getRight().isBlack()) {
w.getLeft().setBlack();
w.setRed();
rotateRight(w);
w = node.getParent().getRight();
}
w.setColor(node.getParent().getColor());
node.getParent().setBlack();
w.getRight().setBlack();
rotateLeft(node.getParent());
node = this.root;
}
}
}
else {
//Repeat up changing left with right
if (node.getParent().hasLeft()) {
RBTreeWParentNode<K, V> w = node.getParent().getLeft();
if (w.isRed()) {
w.setBlack();
node.getParent().setRed();
rotateRight(node.getParent());
w=node.getParent().getLeft();
}
if (w.hasLeft() && w.hasRight() && w.getLeft().isBlack() && w.getRight().isBlack()) {
w.setRed();
node = node.getParent();
}
else {
if (w.hasLeft() && w.getLeft().isBlack()) {
w.getRight().setBlack();
w.setRed();
rotateLeft(w);
w = node.getParent().getLeft();
}
w.setColor(node.getParent().getColor());
node.getParent().setBlack();
w.getLeft().setBlack();
rotateRight(node.getParent());
node = this.root;
}
}
}
}
node.setBlack();
}
#SuppressWarnings("unused")
#Override
public boolean equals(Object other) {
if (!(other instanceof RBTreeWParentDictionary)) {
return false;
}
if ((this == null && other != null) || (this != null && other == null)) {
return false;
}
if (this == null && other == null) {
return true;
}
else {
#SuppressWarnings("unchecked")
RBTreeWParentDictionary<K, V> oth = (RBTreeWParentDictionary<K, V>) other;
return equalsNodes(this.root, oth.root);
}
}
private boolean equalsNodes(RBTreeWParentNode<K, V> node1,
RBTreeWParentNode<K, V> node2) {
if ((node1 == null && node2 != null) || (node1 != null && node2 == null)) {
return false;
}
else if (node1 == null && node2 == null) {
return true;
}
else return node1.equals(node2)
&& equalsNodes(node1.getLeft(), node2.getLeft())
&& equalsNodes(node1.getRight(), node2.getRight());
}
}
RBTreeWParentNode.java (Here is the node of RedBlackTree)
package dictionary;
public class RBTreeWParentNode<K, V> {
private K key;
private V value;
private boolean color;
private RBTreeWParentNode<K, V> left, right, parent;
private static final boolean RED = true;
private static final boolean BLACK = false;
public RBTreeWParentNode(K key, V value, RBTreeWParentNode<K, V> left,
RBTreeWParentNode<K, V> right, RBTreeWParentNode<K, V> parent) {
this.key = key;
this.value = value;
this.color = RED;
this.left = left;
if (this.hasLeft())
this.getLeft().setParent(this);
this.right = right;
if (this.hasRight())
this.getRight().setParent(this);
this.parent = parent;
}
public RBTreeWParentNode(K key, V value) {
this.key = key;
this.value = value;
this.color = RED;
}
public RBTreeWParentNode() {
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public boolean getColor() {
return color;
}
public RBTreeWParentNode<K, V> getLeft() {
return left;
}
public RBTreeWParentNode<K, V> getRight() {
return right;
}
public RBTreeWParentNode<K, V> getParent() {
return parent;
}
public RBTreeWParentNode<K, V> getBrother() {
if (this.hasParent()) {
if (this.getParent().getLeft().equals(this)) {
return this.getParent().getRight();
}
else return this.getParent().getLeft();
}
else return null;
}
public boolean isRed() {
return this.color == RED;
}
public boolean isBlack() {
return this.color == BLACK;
}
public boolean hasLeft() {
return this.getLeft() != null;
}
public boolean hasRight() {
return this.getRight() != null;
}
public boolean hasParent() {
return this.getParent() != null;
}
public boolean hasBrother() {
if (this.hasParent()) {
if (this.getParent().getLeft().equals(this)) {
return this.getParent().getRight() != null;
}
else return this.getParent().getLeft() != null;
}
else return false;
}
public void setKey(K key) {
this.key = key;
}
public void setValue(V value) {
this.value = value;
}
public void setRed() {
this.color = RED;
}
public void setBlack() {
this.color = BLACK;
}
public void setParent(RBTreeWParentNode<K, V> node) {
this.parent = node;
}
public void setLeft(RBTreeWParentNode<K, V> node) {
this.left = node;
if (this.hasLeft())
this.left.setParent(this);
}
public void setRight(RBTreeWParentNode<K, V> node) {
this.right = node;
if (this.hasRight())
this.right.setParent(this);
}
public void setColor(boolean color) {
this.color = color;
}
#Override
public boolean equals(Object other) {
if (!(other instanceof RBTreeWParentNode)) {
return false;
}
if ((this == null && other != null) || (this != null && other == null)) {
return false;
}
#SuppressWarnings("unchecked")
RBTreeWParentNode<K, V> oth = (RBTreeWParentNode<K, V>) other;
return checkFieldsEquals(oth);
}
private boolean checkFieldsEquals(RBTreeWParentNode<K, V> oth) {
//Check keys
if ((this.getKey() == null && oth.getKey() != null)
|| (this.getKey() != null && oth.getKey() == null)) {
return false;
}
else {
if ((this.getKey() == null && oth.getKey() == null)
|| this.getKey().equals(oth.getKey())) {
if ((this.getValue() == null && oth.getValue() != null)
|| (this.getValue() != null && oth.getValue() == null)) {
return false;
}
else {
if ((this.getValue() == null && oth.getValue() == null)
|| (this.getValue().equals(oth.getValue()))) {
if (this.getColor() != oth.getColor()) {
return false;
}
else {
return (this.getKey() == null && oth.getKey() == null)
|| this.getKey().equals(oth.getKey());
}
}
else return false;
}
}
else {
return false;
}
}
}
}
RBTreeWParentDictionaryTest.java -> My test class
Update 09/07/2016
I've updated my code because i found out that I wasn't updating the node cursor to root after the fix-up and I wasn't calling the fix-up only when the deleted node is black.
Considering my test case testDeleteDoubles I've figured out that I'm choosing a wrong candidate to switch with the item to delete when it has a brother.
Seeing this simulator the candidate should be the max node on the left branch of the deleted item, but shouldn't it be the successor, so the min item on the right branch?
In delete(), you need to remember the child node of the deleted node because the red-black properties might be violated after the delete. Let's say we declare RBTreeWParentNode<K, V> childOfDeletedNode;
Then, for the case of left child you update childOfDeletedNode = node.getLeft();
For the case of right child you update childOfDeletedNode = node.getRight();
For both children, you need to add the following after calling min():
oldColor = node.getColor();
childOfDeletedNode = node.getLeft();
node.setColor(tmp.getColor());
For leaf, take any child childOfDeletedNode = node.getRight();
Then, you fix the color of the child node with deleteFixUp(childOfDeletedNode);
Now since childOfDeletedNode can be null, you need to handle that case in deleteFixUp by adding a check for node != null in the loop condition and adding an if-statement before setting the color to black in the last line.
Anyway, the simulator you refer to finds the maximum node of the left sub-tree. Your solution finds the minimum node of the right sub-tree. Both are correct, but will result in different results. You'll need fix your test case.
To illustrate, before the delete:
10(B)
/ \
8(R) 100(B)
/ \
5(B) 9(B)
/ \
2(R) 6(R)
After the delete of 8, you replace it 9, the minimum node of the right sub-tree. The color is changed to red.
10(B)
/ \
9(R) 100(B)
/
5(B)
/ \
2(R) 6(R)
Related
Here is the Node class:
public class BSTNode<T extends Comparable<? super T>> {
private T data;
private BSTNode<T> left;
private BSTNode<T> right;
BSTNode(T data) {
this.data = data;
}
T getData() {
return data;
}
BSTNode<T> getLeft() {
return left;
}
BSTNode<T> getRight() {
return right;
}
void setData(T data) {
this.data = data;
}
void setLeft(BSTNode<T> left) {
this.left = left;
}
void setRight(BSTNode<T> right) {
this.right = right;
}
}
Here is my BST class with main driver method:
import java.util.NoSuchElementException;
public class BST<T extends Comparable<? super T>> {
private BSTNode<T> root;
private int size;
BST() {
root = null;
}
public void add(T data) {
if (data == null) {
throw new IllegalArgumentException("Error: Data can't be null");
}
root = rAdd(root, data);
}
private BSTNode<T> rAdd(BSTNode<T> current, T data) {
if (current == null) {
size++;
return new BSTNode<T>(data);
} else if (data.compareTo(current.getData()) < 0) {
current.setLeft(rAdd(current.getLeft(), data));
} else if (data.compareTo(current.getData()) > 0) {
current.setRight(rAdd(current.getRight(), data));
}
return current;
}
public T remove(T data) {
if (data == null) {
throw new IllegalArgumentException("Error: data can't be null");
}
BSTNode<T> dummy = new BSTNode<>(null);
root = rRemove(root, data, dummy);
return dummy.getData();
}
private BSTNode<T> rRemove(BSTNode<T> current, T data, BSTNode<T> dummy) {
if (current == null) {
throw new NoSuchElementException("Error: Data not present");
} else if (data.compareTo(current.getData()) < 0) {
current.setLeft(rRemove(current.getLeft(), data, dummy));
} else if (data.compareTo(current.getData()) > 0) {
current.setRight(rRemove(current.getRight(), data, dummy));
} else {
System.out.println("Data found ... ");
dummy.setData(current.getData());
size--;
if (current.getRight() == null && current.getLeft() == null) {
if (current.equals(root)) {
this.root = null;
}
return null;
} else if (current.getLeft() != null) {
return current.getLeft();
} else if (current.getRight() != null) {
return current.getRight();
} else {
BSTNode<T> dummy2 = new BSTNode<>(null);
current.setRight(removeSuccessor(current.getRight(), dummy2));
current.setData(dummy2.getData());
}
}
return current;
}
private BSTNode<T> removeSuccessor(BSTNode<T> current, BSTNode<T> dummy) {
if (current.getLeft() == null) {
dummy.setData(current.getData());
return current.getRight();
} else {
current.setLeft(removeSuccessor(current.getLeft(), dummy));
}
}
public List<T> inorder(BSTNode<T> root) {
ArrayList<T> inorderContents = new ArrayList<T>();
if (root == null) {
return inorderContents;
}
inorderR(inorderContents, root);
return inorderContents;
}
private void inorderR(ArrayList<T> inorderContents, BSTNode<T> current) {
if (current == null) {
return;
}
inorderR(inorderContents, current.getLeft());
inorderContents.add(current.getData());
inorderR(inorderContents, current.getRight());
}
public BSTNode<T> getRoot() {
// DO NOT MODIFY THIS METHOD!
return root;
}
public int size() {
// DO NOT MODIFY THIS METHOD!
return size;
}
public static void main(String[] args) {
BST bst3 = new BST<>();
bst3.add(1);
bst3.add(0);
bst3.add(5);
bst3.add(4);
bst3.add(2);
bst3.add(3);
System.out.println(bst3.inorder(bst3.getRoot() ));
bst3.remove(1);
System.out.println(bst3.inorder(bst3.getRoot() ));
}
}
My IDE (IntelliJ) says I am missing a return statement for my removeSuccessor(BSTNode current, BSTNode dummy) method but I expected it to recurse to the base case reinforcing the unchanged nodes.
As a result when I try and remove from a two child node it returns zero although the one child and zero child cases work .
Please can someone tell me what is wrong with my two child node remove case? Thanks, Sperling.
First, you need to modify the if-else by changing the conditions:
if (current.getLeft() != null && current.getRight() == null) {
return current.getLeft();
} else if (current.getRight() != null && current.getLeft() == null) {
return current.getRight();
}
instead of the same without the 2nd arguments of && in the rRemove() method.
Then, use this:
private BSTNode<T> removeSuccessor(BSTNode<T> current, BSTNode<T> dummy) {
if (current.getLeft() == null) {
dummy.setData(current.getData());
return current.getRight();
} else {
current.setLeft(removeSuccessor(current.getLeft(), dummy));
return current;
}
}
Meaning of this method is as follows: delete the lowest value in the tree and return new root, as well as remember the value using dummy.
If we get nothing to the left, it's trivial - we delete current node, return getRight() and set a value to dummy.
On the other hand, if we get something to the left then we know that our current node will be the root, but before we return it we need to remove the lowest entry to the left, and so we use the function recursively, also setting current.left to be it's return value to properly transform the tree. We pass dummy so that it can get a value when the first case occurs, and then it's communicated to the highest call (inside the first function).
It's also possible to do it without recursion:
private BSTNode<T> removeSuccessor2(BSTNode<T> current, BSTNode<T> dummy) {
BSTNode<T> root = current;
BSTNode<T> prev = null;
while(current.getLeft() != null) {
prev = current;
current = current.getLeft();
}
/* current.getLeft == null */
//we will delete current
if (prev == null) { //no loop iterations -- current is the root
dummy.setData(current.getData());
return(current.getRight());
}
else {//some iterations passed, prev.getLeft() == curret
dummy.setData(current.getData());
prev.setLeft(current.getRight());
return root;
}
}
With dummy we return the value, the rest is transforming the tree.
Note: It doesn't work in my version for current == null. You should be able to modify it easily, though. Also, for clarity I didn't pull the dummy.setData... before if...else etc. Modify it as you wish!
I've spent about two days pouring over this and I have no idea what's missing. Originally my BST used comparables, then I switched it to int to simplify it when it wasn't working.
I add several items to a tree and it successfully prints them out in order. Then, the first time I call the search() method it returns true, as it should. Every other search call after that returns false whether it is true or false.
I'm including most of my code here in case the problem isn't related with the search method itself.
The output SHOULD be: 4 12 23 27 30 42 60 84 true true false true true
but instead I get: 4 12 23 27 30 42 60 84 true false false false false
public class BSTree {
TreeNode root;
static int comparison;
public void insert(int value) {
if (root == null) {
root = new TreeNode(value);
}
else {
root.insert(value);
}
}
public boolean search(int chicken) {
if (root != null ) {
return root.search(chicken);
}
return false;
}
public static int height(TreeNode b) {
return TreeNode.height(b);
}
public static void CompareSet() {
comparison++;
}
public int getCompare() {
return comparison;
}
public void ResetCompare() {
comparison = 0;
}
public static void traverseInOrder (TreeNode node) {
if (node != null) {
traverseInOrder(node.left);
System.out.print(" " + node.data);
traverseInOrder (node.right);
}
}
public static void main(String args[]) {
BSTree tree = new BSTree();
tree.insert(30);
tree.insert(42);
tree.insert(84);
tree.insert(12);
tree.insert(4);
tree.insert(23);
tree.insert(27);
tree.insert(60);
traverseInOrder(tree.root);
System.out.println("\n" + tree.search(30));
System.out.println("\n" + tree.search(4));
System.out.println("" + tree.search(50));
System.out.println("" + tree.search(27));
System.out.println("" + tree.search(42));
System.out.println(height(tree.root));
}
}
Here is the treeNode class:
public class TreeNode<T> {
int data;
TreeNode left;
TreeNode right;
TreeNode(int value){
this.data = value;
//right = null;
//left = null;
}
public void insert(int value) {
if (value == data) {
return;
}
if (value < data) {
if (left == null) {
left = new TreeNode(value);
}
else {
left.insert(value);
}
}
else {
if (right == null) {
right = new TreeNode(value);
}
else {
right.insert(value);
}
}
public boolean search(int value) {
BSTree.CompareSet();
if (data == value) return true;
if (data < value && left!=null)
return left.search(value);
else if(data > value && right != null)
return right.search(value);
return false;
}
public static int height(TreeNode b) {
if (b == null) return -1;
return 1 + Math.max(height(b.left), height(b.right));
}
public int getData(){
return data;
}
public TreeNode getLeftChild() {
return left;
}
public void setLeftChild(TreeNode leftChild) {
this.left = leftChild;
}
public TreeNode getRightChild() {
return right;
}
public void setRightChild(TreeNode rightChild) {
this.right = rightChild;
}
}
Your comparaisons are reversed :))) Here is the corrected TreeNode::search method :
public boolean search(int value) {
BSTree.CompareSet();
if (data == value) return true;
if (data > value && left!=null)
return left.search(value);
else if(data < value && right != null)
return right.search(value);
return false;
}
I think you swapped the data and the value variables.
just change your search method of TreeNode class to this:
public boolean search(int value) {
Test.CompareSet();
if (data == value) return true;
if (data < value && right!=null)
return right.search(value);
else if(data > value && left != null)
return left.search(value);
return false;
}
When your current node's data is greater than value you need to go to left sub-tree. And when current node's data is smaller than value you need to go to right sub-tree. You just have done the reverse one.
I'm tasked to implement a remove() method for my Sorted Binary Tree. I need to pass a set of tests which was assigned by our teacher, and I'm struggling to do so. This is the code I have currently, which was heavily borrowed from our schoolbook.
#Override
#SuppressWarnings("unchecked")
public V remove(Object key) throws NoSuchElementException {
if(!containsKey((K) key)){
throw new NoSuchElementException();
}
ReturnObject oldEntry = new ReturnObject(null);
SortedTreeMap<K, V> newRoot = removeEntry(getRoot(this), (K) key, oldEntry);
if(newRoot != null){
setRoot(newRoot.data);
setLeftChild(newRoot.leftChild);
setRightChild(newRoot.rightChild);
} else{
this.data = null;
}
if(oldEntry.get() != null){
size--;
}
return oldEntry.get();
}
/*
* Removes the entry in a given root node of a subtree.
* rootNode is the root node of the subtree.
* Returns the root node of the revised subtree.
*
* */
private SortedTreeMap<K,V> removeFromRoot(SortedTreeMap<K, V> rootNode){
if(rootNode.hasLeftChild() && rootNode.hasRightChild()){
SortedTreeMap<K,V> leftSubtreeRoot = rootNode.getLeftChild();
SortedTreeMap<K,V> largestNode = findLargest(leftSubtreeRoot);
rootNode.setRoot(largestNode.getRoot());
rootNode.setLeftChild(removeLargest(leftSubtreeRoot));
} else if(rootNode.hasRightChild()) {
rootNode = rootNode.getRightChild();
} else{
rootNode = rootNode.getLeftChild();
}
return rootNode;
}
/*
* Finds the node containing the largest entry in a given tree.
* rootNode is the root node of the tree.
* Returns the node containing the largest entry in the tree.
*
* */
private SortedTreeMap<K,V> findLargest(SortedTreeMap<K, V> rootNode){
if(rootNode.hasRightChild()){
rootNode = findLargest(rootNode.getRightChild());
}
return rootNode;
}
/*
* Removes the node containing the largest entry in a given tree.
* rootNode is the root node of the tree.
* Returns the root node of the revised tree.
*
* */
private SortedTreeMap<K,V> removeLargest(SortedTreeMap<K, V> rootNode){
if(rootNode.hasRightChild()){
SortedTreeMap<K,V> rightEntry = rootNode.getRightChild();
rightEntry = removeLargest(rightEntry);
rootNode.setRightChild(rightEntry);
} else{
rootNode = rootNode.getLeftChild();
}
return rootNode;
}
/*
* Removes an entry from the tree rooted at a given node.
* rootNode is a reference to the root of a tree.
* entry is the object to be removed.
* oldEntry is an object whose data field is null
* Returns the root node of the resulting tree; if entry matches
* an entry in the tree, oldEntry's data field is the entry
* that was removed from the tree; otherwise it is null.
* */
private SortedTreeMap<K,V> removeEntry(SortedTreeMap<K,V> rootNode, K entry, ReturnObject oldEntry){
if(rootNode != null){
K rootData = rootNode.data.key;
int comparison = entry.compareTo(rootData);
if(comparison == 0){
oldEntry.set(rootNode.data.value);
rootNode = removeFromRoot(rootNode);
} else if(comparison < 0){
SortedTreeMap<K,V> leftEntry = rootNode.getLeftChild();
SortedTreeMap<K,V> subtreeRoot = removeEntry(leftEntry, entry, oldEntry);
rootNode.setLeftChild(subtreeRoot);
} else{
SortedTreeMap<K,V> rightEntry = rootNode.getRightChild();
rootNode.setRightChild(removeEntry(rightEntry, entry, oldEntry));
}
}
return rootNode;
}
And this is the test I'm struggling to pass:
/**
* Check that entry is no longer in map after remove
*/
public Property remove_removes_entry() {
return property(isKVList,
kvs -> implies(kvs.length() > 0,
() -> property(choose(0, kvs.length()-1),
i -> {
P2<Integer, String> entry = kvs.index(i);
SortedTreeMap<Integer, String> tm = new SortedTreeMap<>(intOrd.toComparator());
kvs.foreachDoEffect(kv -> tm.add(kv._1(), kv._2()));
tm.remove(entry._1());
List<Integer> keys = fromIterator(tm.keys().iterator());
return prop(!keys.exists(key -> key.equals(entry._1())));
})
)
);
}
The error message I'm getting is this:
java.lang.Error: Falsified after 2 passed tests with arguments:
List(List((0,gzprxt),(4,lntpqj),(-5,caki),(-6,jzf)),2)
I can get in between 0 to 60 passes and I don't get which scenario the remove method fails in. I've tried to make a Junit test, but didn't succeed.
Here is some more context for my class:
public class Entry<K, V> {
public final K key;
public final V value;
public Entry(K key, V value) {
this.key = key;
this.value = value;
}
#Override
public boolean equals(Object o) {
if (o instanceof Entry) {
Entry other = (Entry)o;
return this.key.equals(other.key) && this.value.equals(other.value);
}
return false;
}
}
public class SortedTreeMap<K extends Comparable<? super K>, V> implements ISortedTreeMap<K, V> {
private int size;
private Entry<K,V> data;
private SortedTreeMap<K,V> leftChild, rightChild, parent;
private Comparator<K> keyComparator;
private SortedTreeMap(K key, V value, Comparator<K> keyComparator, SortedTreeMap<K, V> parent){
data = new Entry<>(key, value);
this.parent = parent;
this.keyComparator = keyComparator;
}
public SortedTreeMap(Comparator<K> kComparator) {
keyComparator = kComparator;
}
private Entry<K,V> getRoot(){
return this.data;
}
private SortedTreeMap<K,V> getLeftChild(){
return this.leftChild;
}
private SortedTreeMap<K,V> getRightChild(){
return this.rightChild;
}
private void setLeftChild(SortedTreeMap<K, V> newLeftChild){
leftChild = newLeftChild;
}
private void setRightChild(SortedTreeMap<K, V> newRightChild){
rightChild = newRightChild;
}
private void setRoot(Entry<K, V> newRoot){
data = newRoot;
}
private boolean hasLeftChild(){
return leftChild != null;
}
private boolean hasRightChild(){
return rightChild != null;
}
#Override
public V add(K key, V value) {
V result = null;
if(isEmpty()) {
data = new Entry<>(key, value);
} else{
int comparison = keyComparator.compare(key, data.key);
SortedTreeMap<K,V> newChild = new SortedTreeMap<>(key, value, keyComparator, this);
if(comparison < 0){
if (hasLeftChild()) {
result = leftChild.add(key, value);
} else{
setLeftChild(newChild);
}
} else if(comparison > 0){
if (hasRightChild()) {
result = rightChild.add(key, value);
} else{
setRightChild(newChild);
}
} else{
result = data.value;
this.data = new Entry<>(key, value);
}
}
if (result == null){
size++;
}
return result;
}
#Override
public Iterable<K> keys() {
return new KeyIterator(this);
}
public class KeyIterator implements Iterable<K>, Iterator<K>{
private SortedTreeMap<K,V> next;
KeyIterator(SortedTreeMap<K, V> root){
next = root;
while(next.leftChild != null){
next = next.leftChild;
}
}
#Override
public Iterator<K> iterator() {
return this;
}
public boolean hasNext(){
return next != null && !next.isEmpty();
}
public K next(){
if(!hasNext()){
throw new NoSuchElementException();
}
SortedTreeMap<K,V> r = next;
if(next.rightChild != null){
next = next.rightChild;
while(next.leftChild != null){
next = next.leftChild;
}
return r.data.key;
}
while(true){
if(next.parent == null){
next = null;
return r.data.key;
}
if(next.parent.leftChild == next){
next = next.parent;
return r.data.key;
}
next = next.parent;
}
}
}
This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 7 years ago.
So in simpletons, I am creating my own AVLTree data structure. Now when i add a new node into my tree, it seems to add fine.
EDIT: It doesnt seem to take into account my duplicates (nor add them to the original node's list by key).
But when i print the rootNode to see if it exists it doesn't exist. I can't figure out what the problem is with my add method.
Here is my AVLTree class:
package cw.util;
import java.util.ArrayList;
import java.util.Comparator;
public class AVLTree<K, V>
{
public class Node {
private K key;
private ArrayList<V> valuesList;
private Node left, right;
private int height;
public Node(K key, ArrayList<V> valuesList) {
this.key = key;
this.valuesList = valuesList;
this.height = 0;
}
public Node(V value) {
}
public void addToNode(V value) {
valuesList.add(value);
}
public K getKey() {
return key;
}
public ArrayList<V> getValues() {
return valuesList;
}
public Node getLeftChild() {
return left;
}
public Node getRightChild() {
return right;
}
public int getHeight() {
return height;
}
public Node getChildNodeFromSide(String side) {
switch(side) {
default: return null;
case "left": return left;
case "right": return right;
}
}
}
private Node rootNode;
private Comparator<K> comparator;
//Unused
public AVLTree() {
}
public AVLTree(Comparator<K> comparator) {
this.comparator = comparator;
this.rootNode = null;
}
public V insert(K key, V value) {
Node n = insert(key, value, rootNode);
if(n != null) {
for(V v : n.getValues())
System.out.println(v.toString());
System.out.println();
return value;
} else {
return null;
}
}
public Node insert(K key, V value, Node node) {
ArrayList<V> values = new ArrayList<V>();
values.add(value);
if(node == null)
node = new Node(key, values);
else if(comparator.compare(key, node.key) < 0) {
node.left = insert(key, value, node.left);
if(height(node.left) - height(node.right) == 2) {
if(comparator.compare(key, node.left.key) < 0)
node = rotateWithLeftChild(node);
else
node = doubleRotateWithLeft(node);
}
} else if(comparator.compare(key, node.key) > 0) {
node.right = insert(key, value, node.right);
if(height(node.right) - height(node.left) == 2) {
if(comparator.compare(key, node.right.key) > 0)
node = rotateWithRightChild(node);
else
node = doubleRotateWithRight(node);
}
} else node.getValues().add(value);
node.height = Math.max(height(node.left), height(node.right)) + 1;
return node;
}
public Node search(K key) {
return search(key, rootNode);
}
public Node search(K key, Node node) {
boolean isFound = false;
while((node != null) && !isFound) {
K nodeKey = node.getKey();
if(comparator.compare(key, nodeKey) < 0)
node = node.getLeftChild();
else if(comparator.compare(key, nodeKey) > 0)
node = node.getRightChild();
else {
isFound = true;
}
node = search(key, node);
}
if(isFound) return node;
else return null;
}
//Custom Methods
public boolean isEmpty() {
return rootNode == null;
}
private int height(Node n) {
return n == null ? -1 : n.getHeight();
}
private Node rotateWithLeftChild(Node node2) {
Node node1 = node2.left;
node2.left = node1.right;
node1.right = node2;
node2.height = Math.max(height(node2.left), height(node2.right)) + 1;
node1.height = Math.max(height(node1.left), node2.getHeight()) + 1;
return node1;
}
private Node rotateWithRightChild(Node node1) {
Node node2 = node1.right;
node1.right = node2.left;
node2.left = node1;
node1.height = Math.max(height(node1.left), height(node1.right)) + 1;
node2.height = Math.max(height(node2.left), node1.getHeight()) + 1;
return node2;
}
private Node doubleRotateWithLeft(Node node) {
node.left = rotateWithRightChild(node.left);
return rotateWithLeftChild(node);
}
private Node doubleRotateWithRight(Node node) {
node.right = rotateWithLeftChild(node.right);
return rotateWithRightChild(node);
}
}
Here is how I test the class:
package cw.avl;
import cw.util.AVLTree;
public class AVLTest
{
public static void main(String[] args) {
AVLTree<String, Integer> tree = new AVLTree<String, Integer>(String.CASE_INSENSITIVE_ORDER);
for (int i=1; i <= 10;i++) {
String s = "S" + i;
int x = i;
tree.insert(s, x);
tree.insert(s, x);
}
}
}
Well, you don't seem to ever assign to rootNode, so it starts null and remains so. In fact, your methods create nodes and return them:
if(node == null)
node = new Node(key, values);
...
return node
But you don't use the returned node.
Edit: longer explanation:
When you call from the other function like this: Node n = insert(key, value, rootNode); you are basically saying: Node n = insert(key, value, null);. On the receiving end, here:
public Node insert(K key, V value, Node node) { ,
you are creating a new variable called node with initial value null. Then you replace that value when you do:
node = new Node(key, values);
That value is for the node variable in the insert(K,V,N) method, in no way is rootNode retroactively updated. You could just do so right there:
if(node == null) {
node = new Node(key, values);
rootNode = node;
}
I'm trying to write an implementation of a BinaryTree whose object can be of any type that implements Comparable. However, I realize that won't completely work. For example, A String and a Double wouldn't be able to be inserted into the same tree, even though they both implement Comparable.
So, I would like to know if it's possible to write the code such that the BinaryTree can be instantiated with any value whose type implements Comparable, but any ensuing elements added to the tree must all share the same supertype as the root's value.
Here's the code I have so far:
public class BinaryTree {
private Node root;
public BinaryTree() {
this.root = null;
}
public Node lookup(Comparable<Object> value) {
return lookup(this.root, value);
}
private Node lookup(Node node, Comparable<Object> value) {
Node match = null;
if (match != node) {
if (value == node.value) {
match = node;
} else if (value.compareTo(node.value) < 0) {
return lookup(node.left, value);
} else {
return lookup(node.right, value);
}
}
return match;
}
public Node lookupNonRecursively(Comparable<Object> value) {
return lookupNonRecursively(this.root, value);
}
private Node lookupNonRecursively(Node node, Comparable<Object> value) {
Node match = null;
if (match != node) {
if (value == node.value) {
match = node;
} else {
Node root = node;
boolean found = false;
while (!found && root != null) {
if (root.value.compareTo(value) < 0) {
if (root.left == null) {
root.left = match = new Node(value);
found = true;
} else {
root = root.left;
}
} else {
if (root.right == null) {
root.right = match = new Node(value);
found = true;
} else {
root = root.right;
}
}
}
}
}
return match;
}
public Node insert(Comparable<Object> value) {
return insert(this.root, value);
}
private Node insert(Node node, Comparable<Object> value) {
if (node == null) {
node = new Node(value);
} else {
if (node.value.compareTo(value) <= 0) {
insert(node.left, value);
} else {
insert(node.right, value);
}
}
return node;
}
public Node insertNonRecursively(Comparable<Object> value) {
return insertNonRecursively(this.root, value);
}
private Node insertNonRecursively(Node node, Comparable<Object> value) {
if (node == null) {
node = new Node(value);
} else {
Node root = node;
boolean inserted = false;
while (!inserted) {
if (node.value.compareTo(root.value) < 0) {
if (root.left == null) {
root.left = node = new Node(value);
inserted = true;
} else {
root = root.left;
}
} else {
if (root.right == null) {
root.right = node = new Node(value);
inserted = true;
} else {
root = root.right;
}
}
}
}
return node;
}
public static class Node {
private Node left;
private Node right;
private Comparable<Object> value;
public Node(Comparable<Object> value) {
this.left = null;
this.right = null;
this.value = value;
}
}
}
And as a test, this will throw the error, The method insert(Comparable<Object>) in the type BinaryTree is not applicable for the arguments (Integer), if I try to run code like the following:
BinaryTree tree = new BinaryTree();
tree.insert(new Integer(1));
You can see I've implemented some different BinaryTree methods for this class, but the same rules would need to apply: any value passed into lookup() or insert() would also need to share the root's supertype. I have a feeling this is where some variant of <T extends Comparable<? super T>> is going to come into play, but my mind is just not figuring this one out.
Any ideas for how I might accomplish this?
As noted by #jp-jee, here's my solution (also with logic and other bugs fixed from untested first attempt), which works beautifully:
public class BinaryTree<T extends Comparable<T>> {
private Node<T> root;
public BinaryTree() {
this.root = null;
}
public Node<T> lookup(T value) {
return lookup(this.root, value);
}
private Node<T> lookup(Node<T> node, T value) {
Node<T> match = null;
if (match != node) {
if (value.equals(node.value)) {
match = node;
} else if (value.compareTo(node.value) < 0) {
return lookup(node.left, value);
} else {
return lookup(node.right, value);
}
}
return match;
}
public Node<T> lookupNonRecursively(T value) {
return lookupNonRecursively(this.root, value);
}
private Node<T> lookupNonRecursively(Node<T> node, T value) {
Node<T> match = null;
if (match != node && value != null) {
if (value.equals(node.value)) {
match = node;
} else {
Node<T> searchRoot = node;
boolean found = false;
while (!found && searchRoot != null) {
if (value.equals(searchRoot.value)) {
match = searchRoot;
found = true;
} else if (value.compareTo(searchRoot.value) < 0) {
searchRoot = searchRoot.left;
} else {
searchRoot = searchRoot.right;
}
}
}
}
return match;
}
public void insert(T value) {
this.root = insert(this.root, value);
}
private Node<T> insert(Node<T> node, T value) {
if (node == null) {
node = new Node<T>(value);
} else {
if (value.compareTo(node.value) <= 0) {
node.left = insert(node.left, value);
} else {
node.right = insert(node.right, value);
}
}
return node;
}
public void insertNonRecursively(T value) {
this.root = insertNonRecursively(this.root, value);
}
private Node<T> insertNonRecursively(Node<T> node, T value) {
if (node == null) {
node = new Node<T>(value);
} else {
Node<T> runner = node;
boolean inserted = false;
while (!inserted) {
if (value.compareTo(runner.value) < 0) {
if (runner.left == null) {
runner.left = new Node<T>(value);
inserted = true;
} else {
runner = runner.left;
}
} else {
if (runner.right == null) {
runner.right = new Node<T>(value);
inserted = true;
} else {
runner = runner.right;
}
}
}
}
return node;
}
public static class Node<T extends Comparable<T>> {
private Node<T> left;
private Node<T> right;
private T value;
public Node(T value) {
this.left = null;
this.right = null;
this.value = value;
}
public Node<T> getLeft() {
return left;
}
public Node<T> getRight() {
return right;
}
public T getValue() {
return value;
}
}
}
Make your Binary Tree generic like
public class BinaryTree<T extends Comparable<T>>{
...
}
Whenever creating a BinaryTree instance, specify the containied type:
new BinaryTree<MyClass>();
Where MyClass must implement Comparable<MyClass>, i.e. be comparable to Objects of the same class.
Your methods would read as (example):
public Node lookup(T value) { ... }
The same applies for your Node class. Make it generic the same way.