I am trying to remove nodes from a Binary Search Tree. I can successfully remove any other node on the tree except for one particular case. If the targeted node has 2 children, and the left child has a right subtree, I can locate the correct replacement Node and switch the value to the targeted Node, but then the replacement node is never deleted.
Looking at the picture above, if I try to delete 17, the program will correctly navigate to 13 and replace 17 with 13, but it will not then delete the original 13 as it is supposed to.
I have attached my remove methods and those referenced within.
public Node root;
public void delete(int value){
Node node = new Node<>(value);
Node temp;
if(root == null) {
System.out.println("The tree is already empty!"); //tree is empty
return;
}
if (root.value == node.value) { //Root is target value
temp = node.left;
if(temp.right == null){
node.value = temp.value;
temp = null;
}
else{
while(temp.right != null){
temp = temp.right;
}
node.value = temp.value;
temp = null;
}
return;
}
deleteRec(root, node);
}
private void deleteRec(Node lastRoot, Node node){
Node temp;
if (lastRoot.value >= node.value){
if (lastRoot.left.value == node.value){
node = lastRoot.left;
if(node.left == null && node.right == null){ //No children
node = null;
lastRoot.left = null;
}
else if(node.left == null && node.right != null){ //Right Child
lastRoot.left = node.right;
node = null;
lastRoot.left = null;
}
else if(node.left != null && node.right == null){ //Left Child
lastRoot.left = node.left;
node = null;
}
else{ //Two Children
if(node.left.right == null){
node.value = node.left.value;
node.left = node.left.left;
node.left = null;
}
else{
node = findReplacement(node.left);
lastRoot.left.value = node.value;
node.left = null;
}
}
}
else{
deleteRec(lastRoot.left, node);
}
}
else{
if (lastRoot.right.value == node.value){
node = lastRoot.right;
if(node.left == null && node.right == null){ //No Children
node = null;
lastRoot.right = null;
}
else if(node.left == null && node.right != null){ //Right Child
lastRoot.left = node.right;
node = null;
lastRoot.right = null;
}
else if(node.left != null && node.right == null){ //Left Child
lastRoot.right = node.left;
node = null;
}
else{ //Two Children
if(node.left.right == null){
node.value = node.left.value;
node.left = node.left.left;
node.left = null;
}
else{
node = findReplacement(node.left);
lastRoot.left.value = node.value;
node.left = null;
}
}
}
else{
deleteRec(lastRoot.right, node);
}
}
}
private Node findReplacement(Node node) {
while(node.right != null){
node = node.right;
}
return node;
}
And here is my Node class:
public class Node<T> {
public int value;
public Node left;
public Node right;
public Node parent;
public Node(int value) {
this.value = value;
}
}
Consider this part of your code:
Node rep = findReplacement(node.left);
node.value = rep.value;
rep = null;
You're finding the replacement, and making rep point to it. Then, essentially what you're doing is making rep point to null. This doesn't remove the node! The parent is still pointing to it!
There are several places in your code where you're doing something along these lines. The way you're expected to remove nodes from a tree in this Java implementation is by changing what parents point to. The garbage collector takes care of the other details. I hope addressing this issue helps you resolve your problem!
Related
So I have created a delete function for my BST implementation. It works properly for cases of deleting leaf nodes, and subtrees with a right child, but no left child and vice versa.
However, deleting the root node is giving me problems. I am trying to replace the root with its successor, and then set new root's right child equal to the old root's right child.
Example:
50
/ \
/ \
42 63
/ \ /
/ \ 55
21 43
\ \
23 \
/ \ 47
/ \
44 48
If I were to delete node 50 in my program, 55 would be the new root, but then 63 would just disappear, but everything else works fine.
Am I relinking the nodes wrong?
(Starts at "Problem is Here" comment in code)
//How I display my tree
//inprder traversal
private void inOrder(Node curr){
if(curr != null){
//traverse left
inOrder(curr.leftChild);
System.out.print("\n" + curr + "->");
//traverse right
inOrder(curr.rightChild);
}
}
public boolean deleteContact(String key){
//Start at root node
Node currentNode = root;
Node parent = root;
boolean isLeftChld = true;
//loop over left and right subtrees
//finds node to be deleted
while(!key.equals(currentNode.key)){
parent = currentNode;
if(key.compareToIgnoreCase(currentNode.key) < 0){
isLeftChld = true;
currentNode = currentNode.leftChild;
}else{
isLeftChld = false;
currentNode = currentNode.rightChild;
}
//Node never found
if(currentNode == null){
return false;
}
}
//if the node doesnt have children
//go ahead and delete
if(currentNode.leftChild == null && currentNode.rightChild == null){
if(currentNode == root){
root = null;
//Handle case of juss deleting leafs
}else if(isLeftChld){//check if it was a left or right child
//delete it
parent.leftChild = null;
}else{
parent.rightChild = null;
}
//situation of no right child
//handling node dissapearing
} else if(currentNode.rightChild == null){
if(currentNode == root){
root = currentNode.leftChild;
}else if (isLeftChld){
parent.leftChild = currentNode.leftChild;
}else{
parent.rightChild = currentNode.leftChild;
}
//situation of no left child
}else if(currentNode.leftChild == null){
if(currentNode == root){
root = currentNode.rightChild;
}else if (isLeftChld){
parent.leftChild = currentNode.rightChild;
}else{
parent.rightChild = currentNode.leftChild;
}
**Problem is Here**
//situation of two children being involved
//figure out which node will replace which
} else {
//Node temp = currentNode.rightChild;
Node successor = minValue(currentNode.rightChild);
if(currentNode == root){
root = successor;
}else if(isLeftChld){
parent.leftChild = successor;
}else{
parent.rightChild = successor;
}
root.leftChild = currentNode.leftChild;
}
return true;
}
//Function to return min value for a root node
//Used for delete function - finds succesor
private Node minValue(Node root)
{
Node minv = root;
while (root.leftChild != null)
{
minv = root.leftChild;
root = root.leftChild;
}
return minv;
}
You need to assign the right child as well.
//Root is 50 [42, 63]
if(currentNode == root){
root = successor;
//Root is 55 [null, null]
}else if(isLeftChld){
parent.leftChild = successor;
}else{
parent.rightChild = successor;
}
//Root is 55 [42, null]
root.leftChild = currentNode.leftChild;
The following line is missing:
root.rightChild = currentNode.rightChild;
I think I got most of the cases to work except for case 2 (deletion node has only one subtree). My test case is the tree below without the 1, 2, 3 and 4 nodes:
This program is telling me that 6 was deleted from the tree but when I try printing the tree, it still shows that 6 is in the tree.
public class RandomNode {
static int size;
public static class Node {
int data;
Node left;
Node right;
public Node(int data) {
this.data = data;
left = null;
right = null;
size++;
}
}
// delete node in right subtree
public Node delete(Node root, int data) {
// base case - if tree is empty
if (root == null)
return root;
// search for deletion node
else if (data < root.data)
root.left = delete(root.left, data);
else if (data > root.data) {
root.right = delete(root.right, data);
} else {
// case 1: deletion node has no subtrees
if (root.left == null && root.right == null) {
root = null;
size--;
System.out.println(data + " successfully deleted from tree (case1)");
// case 2: deletion node has only one subtree
} else if (root.left != null && root.right == null) {
root = root.left;
size--;
System.out.println(data + " successfully deleted from tree (case2a)");
} else if (root.left == null && root.right != null) {
root = root.left;
size--;
System.out.println(data + " successfully deleted from tree (case2b)");
// case 3: deletion node has two subtrees
// *find min value in right subtree
// *replace deletion node with min value
// *remove the min value from right subtree or else there'll be
// a duplicate
} else if (root.left != null && root.right != null) {
Node temp;
if (root.right.right == null && root.right.left == null)
temp = findMinNode(root.left);
else
temp = findMinNode(root.right);
System.out.println(root.data + " replaced with " + temp.data);
root.data = temp.data;
if (root.right.right == null || root.left.left == null)
root.left = delete(root.left, root.data);
else
root.right = delete(root.right, root.data);
size--;
System.out.println(temp.data + " succesfuly deleted from tree (case3)");
}
}
return root;
}
// find min value in tree
public Node findMinNode(Node root) {
while (root.left != null)
root = root.left;
System.out.println("min value: " + root.data);
return root;
}
public void preOrderTraversal(Node root) {
if (root == null)
return;
preOrderTraversal(root.left);
System.out.println(root.data);
preOrderTraversal(root.right);
}
public static void main(String[] args) {
RandomNode r = new RandomNode();
Node root = new Node(6);
//root.left = new Node(2);
root.right = new Node(9);
//root.left.left = new Node(1);
//root.left.right = new Node(5);
//root.left.right.left = new Node(4);
//root.left.right.left.left = new Node(3);
root.right.left = new Node(8);
root.right.right = new Node(13);
root.right.left.left = new Node(7);
root.right.right.left = new Node(11);
root.right.right.right = new Node(18);
r.delete(root, 6);
r.preOrderTraversal(root);
}
}
When you do root = root.left; and size --, you must also make root.left = null.
Here you are simply making root as root.left but are not making root.left as null.
It's rather hard for me to follow since I'd write it functionally.
However,
if (root.right.right == null || root.left.left == null)
root.left = delete(root.left, root.data);
else
root.right = delete(root.right, root.data);
Is probably wrong, since it should mirror your earlier findMinNode() calls, and thus should probably be
if(root.right.right == null && root.right.left == null)
root.left = delete(root.left, root.data);
There is more wrong when debugging it.
First off, java is pass by value, so if you're deleting the top node (root), your pointer should be updated too. Hence, the call should be root = r.delete(root, 6);
Second, there's another bug. Case 2a assigns root to root.left... which is null. It should be root.right.
Having made those changes, the output then becomes:
6 successfully deleted from tree (case2b)
7
8
9
11
13
18
I have BST and when I want to delete node from BST, nothing happened. Could someone explain why?
Here is my code:
private void delete(int value, Node node) {
if (value<node.value) delete(value, node.left);
else if (value> node.value)
delete(value, node.right);
else {
if (node.left != null && node.right != null) {
int maxFromLeft = findMax(node.left);
node.value = maxFromLeft;
delete(maxFromLeft, node.left);
} else if (node.left != null) {
Node trash = node;
node = node.left;
trash = null;
} else if (node.right != null) {
Node trash = node;
node = node.right;
trash = null;
} else {
node = null;
}
}
}
Consider this part
if (node.left != null) {
Node trash = node;
node = node.left;
trash = null;
}
You need to delete node. This do nothing node = node.left. It just assigns one reference to another. This dosn't need too trash = null.
To delete node you need to disconnect it from it's parent firstly, and connect a one child of a deleted node to the parent and insert other child where it should be!
A method with the signature void delete(int,Node) cannot work. Consider what happens when you try to delete the root from a tree that has only one node: this method cannot delete the node because method parameters are passed by value.
What you can do is return the modified BST from this method:
Node delete(int value, Node node) {
if (value<node.value) node.left = delete(value, node.left);
else if (value> node.value)
node.right = delete(value, node.right);
...I'll leave the rest as an exercise...
return node;
}
I'm trying to make a tree, in a way such that the left child of a terminal(leaf node) node.
link to see what tree should look like.
I've gotten an inorder version of the code i want, its a simple insert function that threads the tree ,but now my problem is changing this code into a preOrder insert.
This i can do, but my main problem is finding the preorder successor, which is all the way in a other sub tree, do any of you guys know a simple way to get the preorder successor?
//in-order insert
if (root == null) { // tree is empty
root = newNode;
return;
}
PreNode<T> p = root, prev = null;
while (p != null) { // find a place to insert newNode;
prev = p;
if (info.compareTo(p.info) < 0)
p = p.left;
else if (!p.hasThread) // go to the right node only if it is
p = p.right; // a descendant, not a successor;
else break; // don't follow successor link;
}
if (info.compareTo(prev.info) < 0) { // if newNode is left child of
prev.left = newNode; // its parent, the parent becomes
newNode.hasThread = true; // also its successor;
newNode.right = prev;
}
else if (prev.hasThread) { // if parent of the newNode
newNode.hasThread = true; // is not the right-most node,
prev.hasThread = false; // make parent's successor
newNode.right = prev.right; // newNode's successor,
prev.right = newNode;
}
else prev.right = newNode; // otherwise has no successor;
This will work, assuming that there is a Node class which has right&left references to its children as well as a boolean variable ht which indicates if it has a thread or not.
public void insert(T info) {
PreNode<T> newNode = new PreNode<T>(info);
if (root == null) {
root = newNode;
return;
}
PreNode<T> curr = root, r = null, l = null;
while (true) {
if (info.compareTo(curr.info) < 0) {
if (curr.right != null)
r = curr.right;
if (curr.left == null || curr.ht) {
newNode.left = r;
curr.left = newNode;
curr.ht = false;
return;
} else
curr = curr.left;
} else {
if (curr.left != null && !curr.ht)
l = curr.left;
if (curr.right == null) {
if (curr.ht) {
newNode.left = curr.left;
newNode.ht = true;
curr.left = null;
curr.right = newNode;
curr.ht = false;
return;
} else
newNode.left = r;
curr.right = newNode;
curr.ht = false;
if (l != null) {
while (!l.ht) {
if (l.right != null)
l = l.right;
else
l = l.left;
}
l.left = newNode;
l.ht = true;
return;
}
} else
curr = curr.right;
}
}
}
I am having some problems printing an inOrder traversal of my binary tree. Even after inserting many items into the tree it's only printing 3 items.
public class BinaryTree {
private TreeNode root;
private int size;
public BinaryTree(){
this.size = 0;
}
public boolean insert(TreeNode node){
if( root == null)
root = node;
else{
TreeNode parent = null;
TreeNode current = root;
while( current != null){
if( node.getData().getValue().compareTo(current.getData().getValue()) <0){
parent = current;
current = current.getLeft();
}
else if( node.getData().getValue().compareTo(current.getData().getValue()) >0){
parent = current;
current = current.getRight();
}
else
return false;
if(node.getData().getValue().compareTo(parent.getData().getValue()) < 0)
parent.setLeft(node);
else
parent.setRight(node);
}
}
size++;
return true;
}
/**
*
*/
public void inOrder(){
inOrder(root);
}
private void inOrder(TreeNode root){
if( root.getLeft() !=null)
this.inOrder(root.getLeft());
System.out.println(root.getData().getValue());
if( root.getRight() != null)
this.inOrder(root.getRight());
}
}
It seems that you are not traversing the tree properly upon insertion, to find the right place for the new node. Right now you are always inserting at one child of the root, because the insertion code is inside the while loop - it should be after it:
public boolean insert(TreeNode node){
if( root == null)
root = node;
else{
TreeNode parent = null;
TreeNode current = root;
while( current != null){
if( node.getData().getValue().compareTo(current.getData().getValue()) <0){
parent = current;
current = current.getLeft();
}
else if( node.getData().getValue().compareTo(current.getData().getValue()) >0){
parent = current;
current = current.getRight();
}
else
return false;
}
if(node.getData().getValue().compareTo(parent.getData().getValue()) < 0)
parent.setLeft(node);
else
parent.setRight(node);
}
size++;
return true;
}
You insert method has a problem. It finds the right parent node to attach the new element to, but on the way it messes up the whole tree. You should move the insertion code out of the while loop:
public boolean insert(TreeNode node){
if( root == null)
root = node;
else{
TreeNode parent = null;
TreeNode current = root;
while( current != null){
if( node.getData().getValue().compareTo(current.getData().getValue()) <0){
parent = current;
current = current.getLeft();
}
else if( node.getData().getValue().compareTo(current.getData().getValue()) >0){
parent = current;
current = current.getRight();
}
else
return false;
}
if(node.getData().getValue().compareTo(parent.getData().getValue()) < 0)
parent.setLeft(node);
else
parent.setRight(node);
}
size++;
return true;
}
}
hey fellows here is one simple one.. try this out.. it works for me well...
public void levelOrderTraversal(Node root){
Queue<Node> queue = new ArrayDeque<Node>();
if(root == null) {
return;
}
Node tempNode = root;
while(tempNode != null) {
System.out.print(tempNode.data + " ");
if(tempNode.left != null) queue.add(tempNode.left);
if(tempNode.right != null) queue.add(tempNode.right);
tempNode = queue.poll();
}
}