I have a Binary Search Tree that contains nodes. Each node contains a key value and data value, and the nodes are sorted by key. I am trying to write a method to remove an object from my BST provided a key. Here is the code:
public Object remove(Comparable theKey) {
return remove(theKey, rootPtr).data;
}
public Node remove(Comparable theKey, Node node) {
Object o;
if(node == null)
return node;
if(theKey.compareTo(node.key) < 0) {
// go to left subtree
node.leftChild = remove(theKey, node.leftChild);
}else if(theKey.compareTo(node.key) > 0) {
//go to the right subtree
node.rightChild = remove(theKey, node.rightChild);
}else if(theKey.compareTo(node.key) == 0) {
if(node.leftChild != null && node.rightChild != null){
Node foundNode = findMin(node.rightChild);
node.key = foundNode.key;
node.data = foundNode.data;
node.rightChild = remove(node.key, node.rightChild);
}else{
if(node.leftChild != null){
node = node.leftChild;
}else{
node = node.rightChild;
}
}
}
numNodes--;
return node;
}
I would like to return the data value associated with the DELETED node. The issue I have is that: in the public Object remove() method, wouldn't the returned value always be the data value of the root node? I believe this would occur because the final returned call from the second method would be a reference to the rootPtr (root pointer). If this is the case, how can I save the data from the deleted node?
The simplest solution seems to be to add an output parameter than hands back the result:
public Object remove(Comparable theKey) {
Object[] result = new Object[1];
rootPtr = remove(theKey, rootPtr, result); // fix for deletion from a one-node tree
return result[0];
}
public Node remove(Comparable theKey, Node node, Object[] result) {
if(node == null) {
return node;
}
int diff = theKey.compareTo(node.key);
if (diff < 0) {
node.leftChild = remove(theKey, node.leftChild, result);
} else if (diff > 0) {
node.rightChild = remove(theKey, node.rightChild, result);
} else {
result[0] = node.key;
if (node.rightChild == null) {
node = node.leftChild;
} else if (node.leftChild == null) {
node = node.rightChild;
} else {
Node foundNode = findMin(node.rightChild);
node.key = foundNode.key;
node.data = foundNode.data;
node.rightChild = remove(node.key, node.rightChild, new Object[1]);
}
numNodes--;
}
return node;
}
Returning the found node doesn't work without significant changes because the return parameter is used to replace nodes as needed, and in the case where the found node has two children, you'd need to make a copy or insert a new node. Handling the case where the root node gets removed would be an issue, too.
p.s. I am assuming this is not a "trick question" and you can't just return theKey -- which has to equal the found key after all :)
You have to return the value, that you are receiving from the recursive remove-call.
p.ex:
if(theKey.compareTo(node.key) < 0) {
// go to left subtree
return remove(theKey, node.leftChild);
}else if ...
Now you will run throught the tree, until you find the correct node and that node will be given to the node which is the parent of the node to remove, and the parent will than give it to his parent and so on, until the root will return the node to his caller.
Related
I'm writing a binary search tree in java with generic classes, but the nodes aren't adding correctly and I can't figure out why.
Here's my insert method (iterative):
public void insert (E element){
//iterative insert element to tree
Node<E> node = new Node<>(element); //create new node for element
Node<E> current = root;
if (root == null) root = node; // if no root, new node is the root
else {
while (current != null){ //traverse tree to get to correct parent node
if (element.compareTo(current.data) < 0) { //case 1: node is smaller than current
if (current.left == null){
node.parent = current;
current.left = node;
}
else {
current = current.left;
}
}
else if (element.compareTo(current.data) > 0) { //case 2: node is larger than current
if (current.right == null){
node.parent = current;
current.right = node;
}
else {
current = current.right;
}
}
else { //case 3: node already exists
throw new RuntimeException("Node already exists");
}
}
}
size++;
}
the problem happens when I'm running my test class. there first node is added and becomes the root, but the next insert after that throws the exception no matter what the value is. It's acting like the value I'm trying to add already exists in the tree. If I comment out the exception, the test class doesn't compile, as if it's running an infinite loop.
What am I doing wrong here?
How would current ever become null in your while loop?
You don't break out from your while loop even when you insert new element:
if (current.left == null) {
node.parent = current;
current.left = node;
}
On the next iteration you assign current to the value you just inserted into current.left:
} else {
current = current.left;
}
and yet on the next iteration you now have current.data equal to element, which leads to the exception. Add break; after you insert the element:
if (current.left == null) {
node.parent = current;
current.left = node;
break;
}
I'm attempting to write a method that would remove node from BST by the given value and I need it to return this deleted value. I found assorted examples of recursive implementations, but because of their nature, they can't return deleted node, but rather the root. Here's what I have now
public TreeNode remove(TreeNode node, int data) {
if (null == node) {
return null;
}
if (data < node.st.getkey()) {
node.left = remove(node.left, data);
} else if (data > node.st.getkey()) {
node.right = remove(node.right, data);
} else { // case for equality
if (node.left != null && node.right != null) {
TreeNode minInRightSubTree = min(node.right);
copyData(node , minInRightSubTree);
node.right = remove(node.right, minInRightSubTree.st.getkey());
} else {
if (node.left == null && node.right == null) {
node = null;
} else {// one child case
TreeNode deleteNode = node;
node = (node.left != null) ? (node.left) : (node.right);
deleteNode = null;
}
}
}
return node;
}
Can I come up with some hack to make it return deleted node or should I look into iterative algorithm( and if so I would really appreciate it if you could hook me up with the link).
You can return not just root but a pair of root and deleted node (or null if nothing was deleted).
You can use Map.Entry or new class to store 2 fields (I would recommend new class since it's more descriptive).
So your possible new signature will be public Map.Entry<TreeNode, TreeNode> remove(TreeNode node, int data)
I have a slightly working binary search tree delete value function. The only time that it does not work is if you are trying to delete a root that has two children.
public boolean delete(BinaryTreeNode node, int i){
if (node == null){
return false;
} else {
if (node.getKey() == i){
BinaryTreeNode parent = new BinaryTreeNode(0, null, null);
parent.setLeftChild(root);
boolean result = deleteHelper(i, node, parent);
root = parent.getLeftChild();
return result;
} else {
return deleteHelper(i, node, null);
}
}
}
public boolean deleteHelper(int value, BinaryTreeNode curr, BinaryTreeNode parent){
if (value < curr.getKey()){
if (curr.getLeftChild() != null){
return deleteHelper(value, curr.getLeftChild(), curr);
} else {
return false;
}
} else if (value > curr.getKey()){
if (curr.getRightChild() != null){
return deleteHelper(value, curr.getRightChild(), curr);
} else {
return false;
}
} else {
if (curr.getRightChild() != null && curr.getLeftChild() != null){
curr.setKey(findMin(curr.getRightChild()).getKey());
deleteHelper(curr.getKey(), curr.getRightChild(), curr);
} else if (parent.getLeftChild() == curr){
parent.setLeftChild((curr.getLeftChild() != null)?curr.getLeftChild():curr.getRightChild());
} else if (parent.getRightChild() == curr){
parent.setRightChild((curr.getLeftChild() != null)?curr.getLeftChild():curr.getRightChild());
}
return true;
}
}
According to Wikipedia
Deleting a node with two children: Call the node to be deleted N. Do not delete N. Instead, choose either its in-order successor node or its in-order predecessor node, R. Copy the value of R to N, then recursively call delete on R until reaching one of the first two cases.
http://en.wikipedia.org/wiki/Binary_search_tree#Deletion
You can try by successor node as follows:
get successor of node to delete (current)
connect parent of current to successor
connect successor to the left child for current
For more details check out this link
Java, Binary tree remove method
This is the last case where the node to be deleted has two children. I cant figure out what I am doing wrong . Please help.
//BTNode has two children
else if (u.getLeft() != null && u.getRight() != null){
//if node to delete is root
BTNode<MyEntry<K,V>> pred = u.getRight();
while (pred.getLeft().element() != null){
pred = pred.getLeft();
}
BTNode<MyEntry<K,V>> predParent = pred.getParent();
if (!hasRightChild(pred)){
predParent.setLeft(new BTNode<MyEntry<K,V>>(null,predParent,null,null));}
if (hasRightChild(pred)){
BTNode<MyEntry<K,V>> predChild = pred.getRight();
predParent.setLeft(predChild);
predChild.setParent(predParent);
}
return returnValue;
ok so modify it like this ??
u.setElement(succ.element());
BTNode<MyEntry<K,V>> succParent = succ.getParent();
if (!hasLeftChild(succ)){
succParent.setRight(new BTNode<MyEntry<K,V>>(null,succParent,null,null));}
if (hasLeftChild(succ)){
BTNode<MyEntry<K,V>> predChild = succ.getLeft();
succParent.setRight(predChild);
predChild.setParent(succParent);
}
return returnValue;
From wikipedia:
Deleting a node with two children: Call the node to be deleted N. Do
not delete N. Instead, choose either its in-order successor node or
its in-order predecessor node, R. Replace the value of N with the
value of R, then delete R.
So, take for example the left children, and then find the rightmost leaf in that subtree, then replace the information of the node to delete with that of the leaf, and then delete that leaf easily.
You might want to create a function that returns the rightmost leaf from a subtree.
I have given the code for deletion of a node in a BST which would work for any condition and that too using a for loop.
public void delete(int key) {
Node<E> temp = find(key);
System.out.println(temp.key);
for (;;) {
// case 1 : external node
if (temp.isExternal()) {
if (temp.getParent().getrChild() == temp) {
temp.parent.rightchild = null;
temp = null;
} else {
temp = null;
}
break;
}
// case2 : one child is null
else if ((temp.getlChild() == null) || (temp.getrChild() == null)) {
if ((temp.parent.leftchild != null) && temp.getParent().getlChild().key == temp.key) {
if (temp.getlChild() == null) {
temp.getParent().setLeft(temp.getrChild());
temp.getrChild().setParent(temp.getParent());
break;
}
else
temp.getParent().setLeft(temp.getlChild());
temp.getlChild().setParent(temp.getParent());
}
else {
if (temp.rightchild != null) {
System.out.println("in");
temp.getParent().setRight(temp.getrChild());
temp.getrChild().setParent(temp.getParent());
break;
}
else
temp.getParent().setRight(temp.getlChild());
temp.getlChild().setParent(temp.getParent());
}
break;
}
// case 3 : has both the children
else {
int t = temp.key;
temp.key = temp.getlChild().key;
temp.getlChild().key = t;
temp = temp.getlChild();
continue;
}
}
}
I have a ordered binary tree:
4
|
|-------|
2 5
|
|-------|
1 3
The leaves point to null. I have to create a doubly link list which should look like
1<->2<->3<->4<->5
(Obviously 5 should point to 1)
The node class is as follows:
class Node {
Node left;
Node right;
int value;
public Node(int value)
{
this.value = value;
left = null;
right = null;
}
}
As you can see the doubly link list is ordered (sorted) as well.
Question: I have to create the linked list form the tree without using any extra pointers. The left pointer of the tree should be the previous pointer of the list and the right pointer of the tree should be the next pointer of the list.
What I thought off: Since the tree is an ordered tree, the inorder traversal would give me a sorted list. But while doing the inorder traversal I am not able to see, where and how to move the pointers to form a doubly linked list.
P.S I checked some variations of this question but none of them gave me any clues.
It sounds like you need a method that accepts a Node reference to the root of the tree and returns a Node reference to the head of a circular list, where no new Node objects are created. I would approach this recursively, starting with the simple tree:
2
|
|-----|
1 3
You don't say whether the tree is guaranteed to be full, so we need to allow for 1 and/or 3 being null. The following method should work for this simple tree:
Node simpleTreeToList(Node root) {
if (root == null) {
return null;
}
Node left = root.left;
Node right = root.right;
Node head;
if (left == null) {
head = root;
} else {
head = left;
left.right = root;
// root.left is already correct
}
if (right == null) {
head.left = root;
root.right = head;
} else {
head.left = right;
right.right = head;
right.left = root;
}
return head;
}
Once it is clear how this works, it isn't too hard to generalize it to a recursive method that works for any tree. It is a very similar method:
Node treeToList(Node root) {
if (root == null) {
return null;
}
Node leftTree = treeToList(root.left);
Node rightTree = treeToList(root.right);
Node head;
if (leftTree == null) {
head = root;
} else {
head = leftTree;
leftTree.left.right = root;
root.left = leftTree.left;
}
if (rightTree == null) {
head.left = root;
root.right = head;
} else {
head.left = rightTree.left;
rightTree.left.right = head;
rightTree.left = root;
root.right = rightTree;
}
return head;
}
If I got all the link assignments covered correctly, this should be all you need.
Do an in-order traversal of the list, adding each list item to the doubly linked list as you encounter it. When done, add an explicit link between the first and last items.
Update 3/6/2012: Since you must reuse the Node objects you already have, after you put the node objects into the the list, you can then iterate over the list and reset the left and right pointers of the Node objects to point to their siblings. Once that is done, you can get rid of the list and simply return the first node object.
This should also work:
NodeLL first = null;
NodeLL last = null;
private void convertToLL(NodeBST root) {
if (root == null) {
return;
}
NodeLL newNode = new NodeLL(root.data);
convertToLL(root.left);
final NodeLL l = last;
last = newNode;
if (l == null)
first = newNode;
else {
l.next = newNode;
last.prev = l;
}
convertToLL(root.right);
}
Let your recursion return the left and right end of formed list. Then you link your current node to the last of the left list, and first of the right list. Basic case it, when there is no left or right element, which is the node it self for both. Once all is done, you can link the first and last of the final result. Below is the java code.
static void convertToSortedList(TreeNode T){
TreeNode[] r = convertToSortedListHelper(T);
r[1].next = r[0];
r[0].prev= r[1];
}
static TreeNode[] convertToSortedListHelper(TreeNode T){
TreeNode[] ret = new TreeNode[2];
if (T == null) return ret;
if (T.left == null && T.right == null){
ret[0] = T;
ret[1] = T;
return ret;
}
TreeNode[] left = TreeNode.convertToSortedListHelper(T.left);
TreeNode[] right = TreeNode.convertToSortedListHelper(T.right);
if (left[1] != null) left[1].next = T;
T.prev = left[1];
T.next = right[0];
if (right[0] != null) right[0].prev = T;
ret[0] = left[0]==null? T:left[0];
ret[1] = right[1]==null? T:right[1];
return ret;
}
Add the following method to your Node class
public Node toLinked() {
Node leftmost = this, rightmost = this;
if (right != null) {
right = right.toLinked();
rightmost = right.left;
right.left = this;
}
if (left != null) {
leftmost = left.toLinked();
left = leftmost.left;
left.right = this;
}
leftmost.left = rightmost;
rightmost.right = leftmost;
return leftmost;
}
EDIT By maintaining the invariant that the list returned by toLinked() has the proper form, you can easily get the left- and rightmost nodes in the sublist returned by the recursive call on the subtrees
/* input: root of BST. Output: first node of a doubly linked sorted circular list. **Constraints**: do it in-place. */
public static Node transform(Node root){
if(root == null){
return null;
}
if(root.isLeaf()){
root.setRight(root);
root.setLeft(root);
return root;
}
Node firstLeft = transform(root.getLeft());
Node firstRight = transform(root.getRight());
Node lastLeft = firstLeft == null ? null : firstLeft.getLeft();
Node lastRight= firstRight == null ? null : firstRight.getLeft();
if(firstLeft != null){
lastLeft.setRight(root);
root.setLeft(lastLeft);
if(lastRight == null){
firstLeft.setLeft(root);
}
else{
firstLeft.setLeft(lastRight);
root.setRight(firstRight);
}
}
if(firstRight != null){
root.setRight(firstRight);
firstRight.setLeft(root);
if(lastLeft == null){
root.setLeft(lastRight);
lastRight.setLeft(root);
firstLeft = root;
}
else{
root.setLeft(lastLeft);
lastRight.setRight(firstLeft);
}
}
return firstLeft;
}