I'm writing a program for class in Java regarding red/black trees. I've got a good understanding of how they usually work, and am supposed to use a recursive insertion method. What I would typically use is below, to match my professor's Node class. In regards to color, a 0 is black, a 1 is red. The Node class given to us does not deal with keys at all.
private static void put(int val, int col)
{ root = put(root, val, col); }
private static Node put(Node n, Integer val, int col)
{
if (n == null){
Node t=new Node(val);
t.setColor(1);
return t;
}
int cmp = val.compareTo(n.getValue());
if (cmp < 0) n.setLeft(put(n.getLeft(), val, col));
else if (cmp > 0) n.setRight(put(n.getRight(), val, col));
else n.setColor(col);
if (isRed(n.getRight()) && !isRed(n.getLeft())) n = rotateLeft(n);
if (isRed(n.getLeft()) && isRed(n.getLeft().getLeft())) n = rotateRight(n);
if (isRed(n.getLeft()) && isRed(n.getRight())) flipColors(n);
return n;
}
However, the catch is that we are supposed to return a boolean value--if the user inserts a duplicate value as is already on the tree, we return false and don't attach the node. Otherwise, we attach them and return true; the code given to us for this is below, but is not recursive (part of the project requirements). And while I hadn't implemented a way of balancing or rotating properly, the returned boolean part works.
public boolean insertNode(Node node) {
//Here is just an example of setting colors for a node. So far, it is in green color. But you need to modify the code to dynamically adjust the color to
//either RED or BLACK according to the red-black logic
Node current_node;
// if the root exists
if (root == null) {
root = node; // let the root point to the current node
root.setColor(Node.BLACK);
return true;
} else {
current_node = root;
node.setColor(1);
while (current_node != null) {
int value = current_node.getValue();
if (node.getValue() < value){ // go to the left sub-tree
if (current_node.getLeft() != null) // if the left node is not empty
current_node = current_node.getLeft();
else{ // put node as the left child of current_node
current_node.setLeft(node);
node.setParent(current_node);
current_node = null; }
//System.out.println("Left:"+current_node);
}
else if (node.getValue() > value){ // go to the right
if (current_node.getRight() != null) // if the right node is not empty
current_node = current_node.getRight();
else{ // put node as the right child of current_node
current_node.setRight(node);
node.setParent(current_node);
current_node = null; }
//System.out.println("Right: "+current_node);
}
else{
//System.out.println("Else: "+current_node);
return false; }
//if(current_node!=null&¤t_node.getLeft()!=null&¤t_node.getRight()!=null&¤t_node.getLeft().isRed()&¤t_node.getRight().isRed())
// flipColors(node);
}
}
if(node.getParent()!=null){
node=node.getParent();
System.out.println("Case: node has parent, val="+node.getValue());
}
if(node.getLeft()!=null&&node.getRight()!=null){
if((node.getRight().isRed())&&!node.getLeft().isRed())
node=rotateLeft(node);
if((node.getLeft().isRed())&&(node.getParent()!=null)&&(node.getParent().getLeft().getLeft()!=null)&&(node.getParent().getLeft().getLeft().isRed()))
node=rotateRight(node);
if((node.getLeft().isRed()) && (node.getRight().isRed()))
flipColors(node);
}
return true;
}
I wasn't able to find any comparable implementations online, and it seems that the boolean is necessary for the program's gui to work properly. If someone has a good suggestion for where to start, I would appreciate it!
For the recursive insertNode, I would suggest you the following: Create a function insertNode(Node node, Node current_node) which returns a boolean value. The idea is to always call the function insertNode for the currently investigated node, starting from the root node. If the node cannot be immediately added to current_node, the responsible node is called recursively to handle the node. I have provided you a short example based on your code (with some comments what the basic idea is, there is obviously some stuff missing). I hope, I got your question correctly and this helps you with your understanding.
public boolean insertNode(Node node) {
if (root == null) {
root = node;
root.setColor(Node.BLACK);
return true;
} else {
boolean result = insertNode(node, root);
if (result) {
//Some other important stuff to do...
}
return result;
}
}
public boolean insertNode(Node node, Node current_node) {
int value = current_node.getValue();
if (node.getValue() < value) {
if (current_node.getLeft() != null) {
// Investigate left
return insertNode(node, current_node.getLeft());
} else {
// Insert node left
return true;
}
} else if (node.getValue() > value) {
if (current_node.getRight() != null) {
// Investigate right
return insertNode(node, current_node.getRight());
} else {
// Insert node right
return true;
}
} else {
return false;
}
}
I now have the working functions, as below:
public boolean insertNode(Node node) {
if(root==null){
root=node;
root.setColor(Node.BLACK);
return true;
}
else
node.setColor(Node.RED);
return insertNode(node, root);
}
public boolean insertNode(Node node, Node cur){
if(node.getValue()<cur.getValue()){
if(cur.getLeft()!=null)
return insertNode(node, cur.getLeft());
else{
cur.setLeft(node);
node.setParent(cur);
handleInsertion(node);
return true; } }
else if(node.getValue()>cur.getValue()){
if(cur.getRight()!=null)
return insertNode(node, cur.getRight());
else{
cur.setRight(node);
node.setParent(cur);
handleInsertion(node);
return true; } }
else
return false;
}
Related
New to Java. I learned BST tree insertion before with C++. But I try to replicate its recursive logic with Java like below:
public void tree_insert(Node x, int k) {
if (x == null)
x = new Node(k);
else if (k < x.val)
tree_insert(x.left, k);
else
tree_insert(x.right, k);
}
Suppose I have an empty tree
tree_insert(tree.root, 1);
But the root has not been changed.... I am guessing it may have to to with pass by reference in Java. But I am not exactly sure why it does not work. Some hint? Thanks.
What you are trying to do is assign a Node that has been passed by reference a new value. You already noted this and that is correct (I see that others stated this too).
How you would do this in java is by splitting up the recursion into two steps. EDIT: Not necessarily in java, but helper functions can be a good design to have in mind.
Introduce the insert function which null checks the root.
Private helper method that can generalise the insertion in the tree with recursive calls.
Here is an example:
public boolean insertBST(int value) {
if (root == null) {
root = new Node(value);
return true;
}
return insertBSTHelper(value, root);
}
private boolean insertBSTHelper(int value, Node node) {
if (node.value == value){
return false;
}
if (node.value > value){
if (node.left != null) {
return insertBSTHelper(value, node.left);
} else {
node.left = new Node(value);
return true;
}
} else {
if (node.right != null) {
return insertBSTHelper(value, node.right);
} else {
node.right = new Node(value);
return true;
}
}
}
Since there can only exist distinct elements in a BST, we check for these to ensure that we don't insert the same element twice.
Hope this helps!
I am wondering how I can switch my remove method from being recursive to being iterative. My recursive method is working perfectly fine, but all my attempts at making it iterative are not. Where am I going wrong and how can I fix it?
So here's my recursive method:
public boolean remove(E someElement) {
return remove(root, someElement);
}
private boolean remove(Node<E> node, E dataItem) {
if (node == null) {
return false;
}
int val = dataItem.compareTo(node.data);
if (val < 0)
return remove(node.left, dataItem);
else if (val > 0)
return remove(node.right, dataItem);
else
return false;
}
BST manipulation is much easier to do iteratively in C/C++ than in Java because of the possibility to get a pointer to a variable.
In Java, you need to treat differently the case where the element is found at the root; in all other cases the node you're considering is either at the left or at the right of it's parent; so you can replace C's pointer (or reference) to pointers with the parent node and a boolean indicating at which side of the parent the current node is:
public boolean remove(E someElement) {
if (root == null) {
return false;
}
int val = someElement.compareTo(root.data);
if (val < 0) {
return remove(root, false, someElement);
} else if (val > 0) {
return remove(root, true, someElement);
} else {
root = removeNode(root);
return true;
}
}
private boolean remove(Node<E> parent, boolean right, E dataItem) {
Node<E> node = right ? parent.right : parent.left;
if (node == null) {
return false;
}
int val = dataItem.compareTo(node.data);
if (val < 0) {
return remove(node, false, dataItem);
} else if (val > 0) {
return remove(node, true, dataItem);
} else {
node = removeNode(node);
if (right) {
parent.right = node;
} else {
parent.left = node;
}
return true;
}
}
I have omitted method removeNode for the time being, right now, we can make the second method iterative:
private boolean remove(Node<E> parent, boolean right, E dataItem) {
while (true) {
Node<E> node = right ? parent.right : parent.left;
if (node == null) {
return false;
}
int val = dataItem.compareTo(node.data);
if (val < 0) {
right = false;
} else if (val > 0) {
right = true;
} else {
node = removeNode(node);
if (right) {
parent.right = node;
} else {
parent.left = node;
}
return true;
}
parent = node;
}
}
Now the method removeNode must remove the top node and return the new top node after removal. If either left or right is null, it can just return the other node, otherwise, we must find a node to replace the topnode, and it can be either the rightmost node of the left subtree, or the leftmode node of the right subtree.
private Node<E> removeNode(Node<E> parent) {
if (parent.left == null) {
return parent.right;
} else if (parent.right == null) {
return parent.left;
}
boolean right = random.nextBoolean();
Node<E> node = right ? parent.right : parent.left;
Node<E> last = removeLast(node, !right);
if (last == null) {
if (right) {
node.left = parent.left;
} else {
node.right = parent.right;
}
return node;
} else {
last.left = parent.left;
last.right = parent.right;
return last;
}
}
private Node<E> removeLast(Node<E> parent, boolean right) {
Node<E> node = right ? parent.right : parent.left;
if (node == null) {
return null;
}
while (true) {
Node<E> next = right ? node.right : node.left;
if (next == null) {
break;
}
parent = node;
node = next;
}
if (right) {
parent.right = node.left;
node.left = null;
} else {
parent.left = node.right;
node.right = null;
}
return node;
}
I'll give you the algorithm, you can try to code it yourself.
You can use a Stack to iterate through the tree.
So here's how you iterate:
push the tree to stack
loop until the stack isn't empty
pop a node
Null check. If null then continue.
push the left and the right sub-tree onto the Stack
Now in the midst of the iteration, you simply need to check if the popped node is the one you are looking for.
Yes? Check if it has children or not.
Has children? Implement the children snatching logic as usual for recursive deletion
Doesn't have children (a.k.a. leaf node)? Simply assign it to null
Break
No? Continue iterating
Although I feel that Trees are by nature recursive and using recursion is simply a better choice in terms of boosting conceptual understanding of the general working principal of this data structure.
As noted in comments, remove as it is now does nothing, and can be safely replaced with return false;.
Assuming that in the else case you want to do something sensible, as in
private boolean remove(Node<E> node, E dataItem) {
if (node == null) {
return false;
}
int val = dataItem.compareTo(node.data);
if (val < 0)
return remove(node.left, dataItem);
else if (val > 0)
return remove(node.right, dataItem);
else
return do_something(node);
}
the standard strategy is to transform it into a tail recursion. Consolidate the multiple recursive calls into a single one, and make it a last statement in the function:
private boolean remove(Node<E> node, E dataItem) {
if (node == null) {
return false;
}
int val = dataItem.compareTo(node.data);
if (val == 0) {
return do_something(node);
}
if (val < 0)
node = node.left;
else
node = node.right;
return remove(node);
}
So far, just a rewrite to achieve a tail recursive form.
Now, any tail recursive function
foo(args) {
if (interesting_condition(args)) {
return do_something_important(args);
}
args = recompute_arguments(args);
return foo(args);
}
could be mechanically transformed into iterative:
foo(args) {
while (!interesting_condition(args)) {
args = recompute_arguments(args);
}
return do_something_important(args);
}
I hope I answered your question.
I have to implement an add method for a BST in java, but can not get my add function to work. Could someone help me out?
private boolean add(E x, BinaryNode<E> currentNode){
if (currentNode == null){
currentNode = new BinaryNode<>(x);
size++;
return true;
}
if (currentNode.element.compareTo(x) == 0){
return false;
}
else if((currentNode.element.compareTo(x) < 0)){
if(currentNode.left == null){
currentNode.left = new BinaryNode<>(x);
size++;
return true;
} else {
add(x, currentNode.left);
}
}
else if(currentNode.element.compareTo(x) > 0){
if(currentNode.right == null){
currentNode.right = new BinaryNode<>(x);
size++;
return true;
} else {
add(x, currentNode.right);
}
}
return false;
}
public boolean add(E x){
return this.add(x, root);
}
One problem I see is that when you assign the root element you assign it to a local variable. This obviously doesn't work.
private boolean add(E x, BinaryNode<E> currentNode){
/////// REMOVE
if (currentNode == null){
currentNode = new BinaryNode<>(x);
size++;
return true;
}
///////
And add this
public boolean add(E x){
if( root == null ) {
root = new BinaryNode<>(x);
size++;
return true;
} else
return this.add(x, root);
}
Basically, the root of subtree might change, and this is a recursion, to make it work the return value should be the new root of the subtree, despite it changed or not.
Following are the add() method taken from my BST impl in Java, that with all test cases passed:
/**
* Add a new value.
*
* #param v
*/
#Override
public void add(T v) {
root = add(root, v);
}
/**
* Add to a subtree start from given node.
*
* #param current root of a subtree to add node to,
* #param v
* #return the new root of subtree,
*/
protected BSTNode<T> add(BSTNode<T> current, T v) {
if (current == null) { // subtree is empty,
size++;
return new BSTNode<>(v);
}
// compare,
int compareFlag = v.compareTo(current.value);
// check this or subtree,
if (compareFlag < 0) { // smaller, go to left subtree,
current.left = add(current.left, v);
} else if (compareFlag > 0) { // larger, go to right subtree,
current.right = add(current.right, v);
} else { // equals, ignore it,
}
return current;
}
I have completed the recursive insert function and it works perfectly, but I can not get the non recursive solution to work.
public void insert(T item){
root= nonRecursive(root,item);
}
public BinaryTreeNode<T> nonRecursive(BinaryTreeNode<T> tree, T item){
if(root==null){
root=new BinaryTreeNode<T>(item);
return root;
}
else{
BinaryTreeNode<T> next = new BinaryTreeNode<T>();
Comparable<T> temp = (Comparable<T>) root.info;
if(temp.compareTo(item)== 0){
return null;
}
else if(temp.compareTo(item) > 0){
next=root.lLink;
}
else{
next=root.rLink;
}
while(next != null){
Comparable<T> temp2 = (Comparable<T>) next.info;
if(temp.compareTo(item) == 0){
return null;
}
else if(temp2.compareTo(item) > 0){
next=next.lLink;
}
else{
next=next.rLink;
}
}
next=new BinaryTreeNode<T>(item);
return root;
}
}
and then the recursive one is:
public void insert(T item) {
root = recInsert(root, item);
}
public BinaryTreeNode<T> recInsert(BinaryTreeNode<T> tree, T item) {
if(tree == null) {
//create new node
tree = new BinaryTreeNode<T>(item);
}
else {
Comparable<T> temp = (Comparable<T>) tree.info;
if (temp.compareTo(item) == 0) {
System.err.print("Already in duplicates are not allowed.");
return null;
}
else if (temp.compareTo(item) > 0)
tree.lLink = recInsert(tree.lLink, item);
else
tree.rLink = recInsert(tree.rLink, item);
}
return tree;
}
does anyone know what I am doing wrong?
I thought I had gotten it but now it only returns the first number I enter in
here you go then
in your code,
if(current == null){
current.lLink=node;
if current is null, then how can it have a iLink ?
maybe you need to do
if(current == null){
current = new Node ();
current.lLink=node;
Your code is not even close to finish.
You haven't even done one comparison. What you did is simply meaningless loops.
If you are looking for a non-recursive logic, here is the pseudo code. Your job is to understand it and write it in Java.
insert(item) {
Node itemNode = new Node(item);
if root is null {
root = itemNode
return
}
currentNode = root;
keep looping until node is inserted {
if currentNode is equals to itemNode {
show error and exit
} else if itemNode is smaller than currentNode {
if (currentNode has no left){
set currentNode's left to itemNode
// Item Inserted!!!!
} else { // there are node at currentNode's left
set currentNode to currentNode's left (and continue lookup)
}
} else { // item node is greater than current node
// do similar thing as the "itemNode < currentNode logic",
// of course on currentNode's right
}
}
}
First off, this is homework, so putting that out there.
I'm supposed to implement a binary search tree with specific methods:
void insert(String), boolean remove(String), and boolean find (String).
I have been able to successfully program and test the insert and find methods but am having difficulty with the remove.
What is going on in my program is the remove isn't actually removing anything from the tree and I believe this is because its only referencing the local creation of the current node but I could be wrong. I think I can implement the logic of the different cases I need to test (might need help with the deleting a node with two children case but I think I get it conceptually) am mainly trying to understand what I need to do differently to reference the tree properly in the
current = null; // case
Here is what I got so far:
public boolean remove(String title)
{
return remove(root, title);
}
private boolean remove(BSTNode current, String title)
{
if (current == null)
{
return false;
}
if (current.data == title)
{
if (current.left_child !=null && current.right_child != null)
{
return true; // returning true since I haven't finished writing this case
}
else if (current.left_child == null && current.right_child == null)
{
current = null; // this should remove the node from tree but it doesn't
return true;
}
else if (current.left_child != null && current.right_child == null)
{
current = current.left_child; // don't think this is right
return true;
}
else if (current.right_child != null && current.left_child == null)
{
current = current.right_child; // not sure about this
return true;
}
}
root = current;
if (title.compareToIgnoreCase(current.data) == -1)
{
return remove(current.left_child, title);
}
else
{
return remove(current.right_child, title);
}
}
Any knowledge is much appreciated.
A node is referenced by it's parent (except for the root, that node is referenced by your BST itself). In order to remove a node from the tree, you need to set that reference in the parent node to null.
What you're trying to do now is something like:
Before:
parent.left ---> node <--- current
After setting current = null:
parent.left ---> node current ---> null
that is, current references null, but that does not change the parent (only that local variable).
In your remove method you need to pass the parent along as well (or handle both children in the call for the parent, whatever you like better).
By the way: never, ever compare strings with ==, see for instance this question.
How to find the node and it's parent without explicitly storing the parent in each node:
I would say it is simpler to do this in a loop, rather than with recursion, but both would work. In a loop:
BSTNode parent = null;
BSTNode current = root;
boolean found = false;
while (!found && current != null) {
int compare = title.compareToIgnoreCase(current.data);
if (compare == 0) {
found = true;
} else {
parent = current;
if (compare < 0) {
current = current.left;
} else {
current = current.right;
}
}
}
if (!found) {
// title was not found
} else if (parent == null) {
// found the root
} else {
// found another node
}
By recursion is annoying, because you want a method that returns both the node and it's parent. You will need some simple inner class to solve this:
private class NodeAndParent {
public BSTNode node;
public BSTNode parent;
public NodeAndParent(BSTNode node, BSTNode parent) {
this.node = node;
this.parent = parent;
}
}
private boolean find(String title, NodeAndParent current) {
if (current.node == null) {
return false; // not found
} else {
int compare = title.compareToIgnoreCase(current.node.data);
if (compare == 0) {
return true; // found
} else {
current.parent = current.node;
if (compare < 0) {
current.node = current.node.left;
} else {
current.node = current.node.right;
}
}
}
}
private boolean remove(String title) {
NodeAndParent nodeAndParent = new NodeAndParent(root, null);
boolean found = find(title, nodeAndParent);
if (!found) {
// title was not found
} else if (nodeAndParent.parent == null) {
// found the root
} else {
// found another node
}
}