This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 5 years ago.
Ok, so I wanted to write a delete method that works for any "delete-case" for Binary Search Trees and this is my attempt:
public void delete(int key) {
if (root.value == key) {
root = root.right;
root.left = root.right.left;
return;
}
else {
deleteRecursive(key, root); ***LINE 58***
}
}
public void deleteRecursive(int key, BinaryNode node) {
if (node == null) {
System.out.println("ERROR");
return;
}
else {
if (key < node.value) {
// continue search on left
if (node.left.value == key) {
if (node.left.left == null && node.left.right == null) {
node.left = null;
}
else if (node.left.left == null){
node.left = node.left.right;
}
else if (node.left.right == null){
node.left = node.left.left;
}
else{
node.left = node.left.right;
node.left.left = node.left.right.left; ***LINE 83***
}
}
else {
deleteRecursive (key, node.left);
}
}
else if (key > node.value){
// continue search on right
if (node.right.value == key) {
if (node.right.left == null && node.right.right == null) {
node.right = null;
}
else if (node.right.left == null){
node.right = node.right.right;
}
else if (node.right.right == null){
}
else if (node.left.left != null && node.left.right != null){
node.right = node.right.right;
node.right.left = node.right.right.left;
}
}
else {
deleteRecursive (key, node.right);
}
}
}
}
But when I actually use the delete method in Main its gives out a NullPointerException.
The error-message reads :
Exception in thread "main" java.lang.NullPointerException
at BinaryTree.deleteRecursive(BinaryTree.java:83)
at BinaryTree.delete(BinaryTree.java:58)
at Main.main(Main.java:14)
So I assume that it is in line 83 and 58 (marked in code).
I've been sitting here for the last hour trying to figure it out and can't seem to get it.
I'm not the best in Java so I thought i could look for some help here! :)
Here are all the files to run the program (everything except for the delete method was already given) : https://www.dropbox.com/sh/r1bt2880hnn6tjm/AADsRsOOzuiNKHp-ZC-IrvVta?dl=0
Since you are trying to remove from Binary Search Tree, better make it totally recursive rather than partially.
private BinaryNode deleteRecursive( int data,BinaryNode node) {
if(node==null){
return null;
}
if(data==node.value)
{
if(node.left!=null && node.right!=null){
BinaryNode minNode = getHighestNodeFromRight(node.right);
node.value = minNode.value;
node.right = deleteRecursive(minNode.value, node.right);
//System.out.println(minNode);
}
else if(node.left==null){
return node.right;
}
else{
return node.left;
}
}
else if(data>node.value){
node.right = deleteRecursive( data,node.right);
}
else{
node.left = deleteRecursive(data, node.left);
}
return node;
}
public BinaryNode getHighestNodeFromRight(BinaryNode node){
if(node.left==null){
return node;
}
else{
BinaryNode n = getHighestNodeFromRight(node.left);
return n;
}
}
Related
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 need to write a method to delete a node from a binary tree. I tried reading and making use of other people's questions, kind of helped. Thing is I get some errors. I'm kind of confused.
Here is the code:
String delete(int k)
{
BSTNode maxfromleft = null;
BSTNode n = getNode(k);
BSTNode n1 = n;
if(n == null)
return null;
if (k < node.getKey())
n.left.setValue(delete(n.left.getKey()));
else if(k > node.getKey()){
n.right.setValue(delete(n.right.getKey()));
}
else{
if(n.right != null && n.left != null){
maxfromleft = max(n.left);
n.left.setValue(delete(n.left.getKey()));
n.setValue(maxfromleft.getValue());
}
else if(n.right == null){
n = n.left;
return n1.getValue();
}
else if(n.left == null){
n = n.right;
return n1.getValue();
}
}
return n.getValue();
}
private BSTNode max(BSTNode n) {
if (n == null)
return null;
if (n.right != null)
return max(n.right);
return n;
}
I get a NullPointerException error at this line:
n.left.setValue(delete(n.left.getKey()));
why?
How can I fix this?
All help is appreciated :)
I am trying to implement a remove method for the BST structure that I have been working on. Here is the code with find, insert, and remove methods:
public class BST {
BSTNode root = new BSTNode("root");
public void insert(BSTNode root, String title){
if(root.title!=null){
if(title==root.title){
//return already in the catalog
}
else if(title.compareTo(root.title)<0){
if(root.leftChild==null){
root.leftChild = new BSTNode(title);
}
else{
insert(root.leftChild,title);
}
}
else if(title.compareTo(root.title)>0){
if(root.rightChild==null){
root.rightChild = new BSTNode(title);
}
else{
insert(root.rightChild,title);
}
}
}
}
public void find(BSTNode root, String title){
if(root!= null){
if(title==root.title){
//return(true);
}
else if(title.compareTo(root.title)<0){
find(root.leftChild, title);
}
else{
find(root.rightChild, title);
}
}
else{
//return false;
}
}
public void remove(BSTNode root, String title){
if(root==null){
return false;
}
if(title==root.title){
if(root.leftChild==null){
root = root.rightChild;
}
else if(root.rightChild==null){
root = root.leftChild;
}
else{
//code if 2 chlidren remove
}
}
else if(title.compareTo(root.title)<0){
remove(root.leftChild, title);
}
else{
remove(root.rightChild, title);
}
}
}
I was told that I could use the insert method to help me with the remove method, but I am just not seeing how I can grab the smallest/largest element, and then replace the one I am deleting with that value, then recursively delete the node that I took the replacement value, while still maintaining O(logn) complexity. Anyone have any ideas or blatant holes I missed, or anything else helpful as I bang my head about this issue?
EDIT:
I used the answers ideas to come up with this, which I believe will work but I'm getting an error that my methods (not just the remove) must return Strings, here is what the code looks like, I thought that's the return statements??
public String remove(BSTNode root, String title){
if(root==null){
return("empty root");
}
if(title==root.title){
if(root.leftChild==null){
if(root.rightChild==null){
root.title = null;
return(title+ "was removed");
}
else{
root = root.rightChild;
return(title+ "was removed");
}
}
else if(root.rightChild==null){
root = root.leftChild;
return(title+ "was removed");
}
else{
String minTitle = minTitle(root);
root.title = minTitle;
remove(root.leftChild,minTitle);
return(title+ "was removed");
}
}
else if(title.compareTo(root.title)<0){
remove(root.leftChild, title);
}
else{
remove(root.rightChild, title);
}
}
public void remove (String key, BSTNode pos)
{
if (pos == null) return;
if (key.compareTo(pos.key)<0)
remove (key, pos.leftChild);
else if (key.compareTo(pos.key)>0)
remove (key, pos.rightChild);
else {
if (pos.leftChild != null && pos.rightChild != null)
{
/* pos has two children */
BSTNode maxFromLeft = findMax (pos.leftChild); //need to make a findMax helper
//"Replacing " pos.key " with " maxFromLeft.key
pos.key = maxFromLeft.key;
remove (maxFromLeft.key, pos.leftChild);
}
else if(pos.leftChild != null) {
/* node pointed by pos has at most one child */
BSTNode trash = pos;
//"Promoting " pos.leftChild.key " to replace " pos.key
pos = pos.leftChild;
trash = null;
}
else if(pos.rightChild != null) {
/* node pointed by pos has at most one child */
BSTNode trash = pos;
/* "Promoting " pos.rightChild.key" to replace " pos.key */
pos = pos.rightChild;
trash = null;
}
else {
pos = null;
}
}
}
This is the remove for an unbalanced tree. I had the code in C++ so I have quickly translated. There may be some minor mistakes though. Does the tree you are coding have to be balanced? I also have the balanced remove if need be. I wasn't quite sure based on the wording of your question. Also make sure you add a private helper function for findMax()
void deleteTreeNode(int data){
root = deleteTreeNode(root ,data);
}
private TreeNode deleteTreeNode(TreeNode root, int data) {
TreeNode cur = root;
if(cur == null){
return cur;
}
if(cur.data > data){
cur.left = deleteTreeNode(cur.left, data);
}else if(cur.data < data){
cur.right = deleteTreeNode(cur.right, data);
}else{
if(cur.left == null && cur.right == null){
cur = null;
}else if(cur.right == null){
cur = cur.left;
}else if(cur.left == null){
cur = cur.right;
}else{
TreeNode temp = findMinFromRight(cur.right);
cur.data = temp.data;
cur.right = deleteTreeNode(cur.right, temp.data);
}
}
return cur;
}
private TreeNode findMinFromRight(TreeNode node) {
while(node.left != null){
node = node.left;
}
return node;
}
To compare objects in java use .equals() method instead of "==" operator
if(title==root.title)
^______see here
you need to use like this
if(title.equals(root.title))
or if you are interesed to ignore the case follow below code
if(title.equalsIgnoreCase(root.title))
private void deleteNode(Node temp, int n) {
if (temp == null)
return;
if (temp.number == n) {
if (temp.left == null || temp.right == null) {
Node current = temp.left == null ? temp.right : temp.left;
if (getParent(temp.number, root).left == temp)
getParent(temp.number, root).left = current;
else
getParent(temp.number, root).right = current;
} else {
Node successor = findMax(temp.left);
int data = successor.number;
deleteNode(temp.left, data);
temp.number = data;
}
} else if (temp.number > n) {
deleteNode(temp.left, n);
} else {
deleteNode(temp.right, n);
}
}
I know this is a very old question but anyways... The accepted answer's implementation is taken from c++, so the idea of pointers still exists which should be changed as there are no pointers in Java. So every time when you change the node to null or something else, that instance of the node is changed but not the original one This implementation is taken from one of the coursera course on algorithms.
public TreeNode deleteBSTNode(int value,TreeNode node)
{
if(node==null)
{
System.out.println("the value " + value + " is not found");
return null;
}
//delete
if(node.data>value) node.left = deleteBSTNode(value,node.left);
else if(node.data<value) node.right = deleteBSTNode(value,node.right);
else{
if(node.isLeaf())
return null;
if(node.right==null)
return node.left;
if(node.left==null)
return node.right;
TreeNode successor = findMax(node.left);
int data = successor.data;
deleteBSTNode(data, node.left);
node.data = data;
}
return node;
}
All the links between the nodes are pertained using the return value from the recursion.
For the Depth First Post-Order traversal and removal, use:
/*
*
* Remove uses
* depth-first Post-order traversal.
*
* The Depth First Post-order traversal follows:
* Left_Child -> Right-Child -> Node convention
*
* Partial Logic was implemented from this source:
* https://stackoverflow.com/questions/19870680/remove-method-binary-search-tree
* by: sanjay
*/
#SuppressWarnings("unchecked")
public BinarySearchTreeVertex<E> remove(BinarySearchTreeVertex<E> rootParameter, E eParameter) {
BinarySearchTreeVertex<E> deleteNode = rootParameter;
if ( deleteNode == null ) {
return deleteNode; }
if ( deleteNode.compareTo(eParameter) == 1 ) {
deleteNode.left_child = remove(deleteNode.left_child, eParameter); }
else if ( deleteNode.compareTo(eParameter) == -1 ) {
deleteNode.right_child = remove(deleteNode.right_child, eParameter); }
else {
if ( deleteNode.left_child == null && deleteNode.right_child == null ) {
deleteNode = null;
}
else if ( deleteNode.right_child == null ) {
deleteNode = deleteNode.left_child; }
else if ( deleteNode.left_child == null ) {
deleteNode = deleteNode.right_child; }
else {
BinarySearchTreeVertex<E> interNode = findMaxLeftBranch( deleteNode.left_child );
deleteNode.e = interNode.e;
deleteNode.left_child = remove(deleteNode.left_child, interNode.e);
}
} return deleteNode; } // End of remove(E e)
/*
* Checking right branch for the swap value
*/
#SuppressWarnings("rawtypes")
public BinarySearchTreeVertex findMaxLeftBranch( BinarySearchTreeVertex vertexParameter ) {
while (vertexParameter.right_child != null ) {
vertexParameter = vertexParameter.right_child; }
return vertexParameter; } // End of findMinRightBranch
I have written a code for inserting to the binary tree an element generic's type which is ordered by their names. Don't think it is correct though.
public boolean insert(E e) {
BTNode temp = root;
if (root == null) {
root.setElement(e);
}
while (temp != null)
if (temp.element().getClass().getName().compareTo(e.getClass().getName()) < 0) {
temp = temp.getRight();
} else {
temp = temp.getLeft();
}
temp.setElement(e);
return true;
}
Can you suggest me corrections ?
An insert would need to create a new node. I don't now how to create them as I haven't see the constuctor, but I suggest something along the lines of:
public boolean insert(E e) {
if (root == null) {
root = new BTNode();
root.setElement(e); //how would this work with a null root?
return true; //that's it, we're done (when is this ever false by the way?)
}
BTNode current = root;
while (true) { //brackets! indenting is important for readabilty
BTNode parent=current;
if (current.element().getClass().getName().compareTo(e.getClass().getName()) < 0) {
current = current.getRight();
if(current==null) { //we don't have a right node, need to make one
current = new BTNode();
parent.setRight(current);
break; //we have a new node in "current" that is empty
}
} else {
current= current.getLeft();
if(current==null) { //we don't have a left node, need to make one
current = new BTNode();
parent.setLeft(current);
break; //we have a new node in "current" that is empty
}
}
}
current.setElement(e);
return true;
}
public Boolean add(int data){
Node node = new Node(data);
if(isEmpty()){
root = node;
}else{
Node temp = root;
while(true){
if(data < temp.getData()){
if(temp.getLeft() != null)
temp = temp.getLeft();
else
break;
}else{
if(temp.getRight() != null)
temp = temp.getRight();
else
break;
}
}
if(data < temp.getData())
temp.setLeft(node);
else
temp.setRight(node);
}
return true;
}
As amadeus mentioned, the while loop should not have a semicolon at the end :
BTNode temp = root;
if (root == null) {
root.setElement(e);
return;
}
while (temp != null)
{
if (temp.element().getClass().getName().compareTo(e.getClass().getName()) < 0) {
if(temp.getRight() != null)
temp = temp.getRight();
else
{
temp.createRight(e);
temp = null; //or break
}
} else {
if(temp.getLeft() != null)
temp = temp.getLeft();
else
{
temp.createLeft(e);
temp = null; //or break
}
}
}
return true;
I'm trying to remove all of the leaves. I know that leaves have no children, this is what I have so far.
public void removeLeaves(BinaryTree n){
if (n.left == null && n.right == null){
n = null;
}
if (n.left != null)
removeLeaves(n.left);
if (n.right != null)
removeLeaves(n.right);
}
n = null; won't help you, since n is just a local variable of your function. Instead, you'd need to set n.left = null; or n.right = null; on the parent.
I won't give you a complete solution, since this smells a lot like homework, but you could, for example, add a return value to your function to indicate whether the node in question is a leaf or not and take appropriate actions in the parent (after the call to removeLeaves).
It's much easier if you break this down like this:
public void removeLeaves(BinaryTree n){
if (n.left != null) {
if (n.left.isLeaf()) {
n.removeLeftChild();
} else {
removeLeaves(n.left);
}
}
// repeat for right child
// ...
}
isLeaf, removeLeftChild and removeRightChild should be trivial to implement.
Instead of n = null, it should be:
if(n.parent != null)
{
if(n.parent.left == n)
{
n.parent.left = null;
}
else if(n.parent.right == n)
{
n.parent.right == null);
}
}
Since Java passes references by values n = null; simply does not work. With this line n was pointing to the leaf and now points to nothing. So you aren't actually removing it from the parent, you are just rerouting a dummy local reference. For the solution do what Matthew suggested.
Here's a simple java method to delete leaf nodes from binary tree
public BinaryTreeNode removeLeafNode(BinaryTreeNode root) {
if (root == null)
return null;
else {
if (root.getLeft() == null && root.getRight() == null) { //if both left and right child are null
root = null; //delete it (by assigning null)
} else {
root.setLeft(removeLeafNode(root.getLeft())); //set new left node
root.setRight(removeLeafNode(root.getRight())); //set new right node
}
return root;
}
}
Easy method with recusrion .
public static Node removeLeaves(Node root){
if (root == null) {
return null;
}
if (root.left == null && root.right == null) {
return null;
}
root.left = removeLeaves(root.left);
root.right = removeLeaves(root.right);
return root;
}
/* #author abhineet*/
public class DeleteLeafNodes {
static class Node{
int data;
Node leftNode;
Node rightNode;
Node(int value){
this.data = value;
this.leftNode = null;
this.rightNode = null;
}
}
public static void main(String[] args) {
Node root = new Node(1);
Node lNode = new Node(2);
lNode.leftNode = new Node(4);
root.leftNode = lNode;
Node rNode = new Node(3);
rNode.rightNode = new Node(5);
root.rightNode = rNode;
printTree(root);
deleteAllLeafNodes(root, null,0);
System.out.println("After deleting leaf nodes::");
printTree(root);
}
public static void deleteAllLeafNodes(Node root, Node parent, int direction){
if(root != null && root.leftNode == null && root.rightNode == null){
if(direction == 0){
parent.leftNode = null;
}else{
parent.rightNode = null;
}
}
if(root != null && (root.leftNode != null || root.rightNode != null)){
deleteAllLeafNodes(root.leftNode, root, 0);
deleteAllLeafNodes(root.rightNode, root, 1);
}
}
public static void printTree(Node root){
if(root != null){
System.out.println(root.data);
printTree(root.leftNode);
printTree(root.rightNode);
}
}
}
This should work-
public boolean removeLeaves(Node n){
boolean isLeaf = false;
if (n.left == null && n.right == null){
return true;
//n = null;
}
if (n!=null && n.left != null){
isLeaf = removeLeaves(n.left);
if(isLeaf) n.left=null; //remove left leaf
}
if (n!=null && n.right != null){
isLeaf = removeLeaves(n.right);
if(b) n.right=null; //remove right leaf
}
return false;
}