I've a binary search tree homework in Java where I was given complete Tree and Node classes and a SearchTree class in which I'm to complete the search and insert methods. The search is supposed to return the value of the node corresponding the searched key.
Here is the Tree class and here the Node class. My search and insert methods are below.
It seems I'm getting close, but test insert of key 0 and value 2 into Tree[Node[0,1,null,null]] results in Tree[Node[0,1,null,null] rather than the correct Tree[Node[0,2,null,null]]. What am I not understanding here?
public static Object search(Tree tree, int key) {
Node x = tree.getRoot();
while (x != null) {
if (key == x.getKey()) {
return x.getValue();
}
if (key < x.getKey()) {
x = x.getLeft();
} else {
x = x.getRight();
}
}
return null;
}
public static void insert(Tree tree, int key, Object value) {
if (tree.getRoot() == null) {
tree.setRoot(new Node(key, value));
}
Node juuri = tree.getRoot();
while (juuri != null) {
if (key == juuri.getKey()) {
return;
} else if (key < juuri.getKey()) {
if (juuri.getLeft() == null) {
juuri.setLeft(new Node(key, value));
return;
} else {
juuri = juuri.getLeft();
}
} else {
if (juuri.getRight() == null) {
juuri.setRight(new Node(key, value));
return;
} else {
juuri = juuri.getRight();
}
}
}
}
Well the issue I am seeing is that key values in your tree are unique. In this if statement you leave your insert method without adding a node if you see that the key is already in the tree.
if (key == juuri.getKey()) {
return;
}
In your example (if I understand it right) 0 is already in the tree so nothing changes when inserting 0 again. Based on your example, I am assuming you want to do an update on a node if the key is the same. So this code would do it.
if (key == juuri.getKey()) {
juuri.setValue(value);
return;
}
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 want to delete a specific node using a key from a Ternary Search Tree. This works pretty well in most cases, but in some of my test sets, nodes that do not have a middle child also do not store values, which shouldn't happen.
I tried different methods I found online, but pretty much all of those few leave the tree in a dirty state, which makes searching a hassle, since you need to check if the leaf you found actually has a value, which shouldn't happen.
Here is my relevant code
private boolean hasChildren(Node x) {
return (x.left != null || x.mid != null || x.right != null);
}
private void reHang(Node x) {
if (hasChildren(x)) {
if (x.left != null) {
x.parent.mid = x.left;
x.left.right = x.right;
} else if (x.right != null) {
x.parent.mid = x.right;
}
}
}
private boolean remove(Node x, String key, int d) {
if (x == null) return false;
char c = key.charAt(d);
if (c < x.c) {
if (!remove(x.left, key, d)) {
x.left = null;
}
} else if (c > x.c) {
if (!remove(x.right, key, d)) {
x.right = null;
}
} else if (d < key.length() - 1) {
if (!remove(x.mid, key, d + 1)) {
x.mid = null;
if (x.val != null) return true;
}
} else {
x.val = null;
}
reHang(x);
return hasChildren(x);
}
private class Node
{
private Value val;
private char c;
private Node left, mid, right, parent;
}
Specifically, problems occur in this function I use for prefix lookups:
private Node getMidPath(Node x) {
if (x.left != null) return getMidPath(x.left);
if (x.mid == null) return x;
return getMidPath(x.mid);
}
Every Node that does not have a x.mid should have an x.val set, which is not always the case after remove, meaning I have dirty nodes.
Any help would be greatly appreciated.
You must see a ternary tree at was it is: Some nested Binary tree but witch each level have only on character as key instead of the whole string. Go down you binary trees until you string is exhausted. Here you will have two eventually references one to the actual data and and one two the actual subtree for longer string with same prefix. Now set the data to null. When the subtree reference is null remove the node. When now the entire subtree is empty continue with the subtree one character earlier. Here set subtree reference to null. Now is both references are null remove the node. When now the entire subtree is empty continue with the subtree one character earlier. And so on
I am making a recursive insert method for a binary tree. This method is not able to add nodes to the tree. i cant seem to find whats wrong with this method. the constructor takes a string label for the child and a parent node.
public void insert(String aLabel) {
//if compare is positive add to right else add to left
//basis case:
BSTreeNode aNode = new BSTreeNode(aLabel,null);
if (aNode.parent == null) {
aNode.parent = this;
}
inserts(this,aNode);
}
private void inserts(BSTreeNode aParent, BSTreeNode aNode){
//initially the root node is the parent however a proper parent is found thorough recursion
//left recursion:
if(aParent.getLabel().compareTo(aNode.getLabel()) <= 0) {
if (this.childrenLeft == null) {
this.childrenLeft = aNode;
aNode.parent = this;
return;
} else {
childrenLeft.inserts(childrenLeft, aNode);
}
}
//right recursion
else {
if (this.childrenRight==null) {
this.childrenRight = aNode;
return;
}
else{
childrenRight.inserts(childrenRight,aNode);
}
}
}
EDIT: This answer refers to the original version of the question.
When you call inserts(this.childrenLeft, aNode); you are still at the same node; i.e. this still refers to the old parent.
Instead you should do something like:
childrenLeft.insert(childrenLeft, aNode);
In fact, the first parameter of insert is redundant, you should refactor to remove it.
I think you may need something like this.
The code is commented so you understand what is going on...
// insert method takes The Node as a param and a value to store in BT
public void insert(Node node, int value) {
//Check that the value param is less than the Node (root) value,
// If so insert the data to the left of the root node. Else insert
// the right node as it is a larger number than root
if (value < node.value) {
if (node.left != null) {
insert(node.left, value);
} else {
System.out.println(" Inserted " + value + " to left of "
+ node.value);
node.left = new Node(value);
}
} else if (value > node.value) {
if (node.right != null) {
insert(node.right, value);
} else {
System.out.println(" Inserted " + value + " to right of "
+ node.value);
node.right = new Node(value);
}
}
}
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
}
}
So this is my first java program, but I've done c++ for a few years. I wrote what I think should work, but in fact it does not. So I had a stipulation of having to write a method for this call:
tree.insertNode(value);
where value is an int.
I wanted to write it recursively, for obvious reasons, so I had to do a work around:
public void insertNode(int key) {
Node temp = new Node(key);
if(root == null) root = temp;
else insertNode(temp);
}
public void insertNode(Node temp) {
if(root == null)
root = temp;
else if(temp.getKey() <= root.getKey())
insertNode(root.getLeft());
else insertNode(root.getRight());
}
Thanks for any advice.
// In java it is little trickier as objects are passed by copy.
// PF my answer below.
// public calling method
public void insertNode(int key) {
root = insertNode(root, new Node(key));
}
// private recursive call
private Node insertNode(Node currentParent, Node newNode) {
if (currentParent == null) {
return newNode;
} else if (newNode.key > currentParent.key) {
currentParent.right = insertNode(currentParent.right, newNode);
} else if (newNode.key < currentParent.key) {
currentParent.left = insertNode(currentParent.left, newNode);
}
return currentParent;
}
Sameer Sukumaran
The code looks a little confusing with overloaded functions. Assuming member variables 'left' and 'right' to be the left child and right child of the BSTree respectively, you can try implementing it in the following way:
public void insert(Node node, int value) {
if (value < node.value)
{
if (node.left != null)
{
insert(node.left, value);
}
else
{
node.left = new Node(value);
}
}
else if (value > node.value)
{
if (node.right != null)
{
insert(node.right, value);
}
else
{
node.right = new Node(value);
}
}
}
........
public static void main(String [] args)
{
BSTree bt = new BSTree();
Node root = new Node(100);
bt.insert(root, 50);
bt.insert(root, 150);
}
You should have a look to this article. It helps to implement a tree structure and search, insert methods:
http://quiz.geeksforgeeks.org/binary-search-tree-set-1-search-and-insertion/
// This method mainly calls insertRec()
void insert(int key) {
root = insertRec(root, key);
}
/* A recursive function to insert a new key in BST */
Node insertRec(Node root, int key) {
/* If the tree is empty, return a new node */
if (root == null) {
root = new Node(key);
return root;
}
/* Otherwise, recur down the tree */
if (key < root.key)
root.left = insertRec(root.left, key);
else if (key > root.key)
root.right = insertRec(root.right, key);
/* return the (unchanged) node pointer */
return root;
}
You can use standard Integer (wrapper for primitive int) object instead of creating a new object type Node. On latest java Integer/int auto-boxing is supported. Hence your method insertNode(int key) can take in Integer argument too (ensure it is not null).
EDIT: Pls ignore above comment. I did not understand your real question. You will have to overload insertNode(). I think you are right.
but where is temp when you insertNode?? With your current implementation temp is lost if root is not null.
I think you want something like
root.getLeft().insertNode(temp);
and
root.getRight().insertNode(temp);
i.e. To insert the new Node (temp) to either the left or the right subtree.