How do I test my delete method in a BST - java

So in my binary search tree, I'm trying to test my delete method to see if it removes a node from the BST. The problem is my test keeps saying that it didn't work.
Here's how I'm testing my delete method
message = "Test 3: deleting 'word' -- ";
t = new BSTRefBased();
try {
t.delete("word");
result = t.getRootItem().getWord().equals(null);
} catch (Exception e) {
result = false;
}
System.out.println(message + (result ? "passed" : "failed"));
Here's my delete method:
public void delete(String word) {
root = deleteItem(root, word);
}
protected TreeNode deleteItem(TreeNode r, String word) {
if (r == null){
return r;
}
if(word.compareTo(r.item.getWord()) < 0){
return r;
} else if (word.compareTo(r.item.getWord()) > 0) {
return r;
} else if(r.left != null && r.right != null)
{
return deleteItem(r, word);
} else {
return r;
}
return r;
}
So why does it keep saying that my delete method failed in my output? Is the problem with my test code or with the actual method? Also I did previously insert the word 'word' in my BST so it should be there.
Here's a pseudo code version of what I want my delete method to do:
delete(treeNode ,searchitem)
targetNode = search(treeNode ,searchItem)
if targetNode is null
return
P = parent node of target Node
if targetNode has no children
update ref in P that leads to targetNode
return
if targetNode has only one child C update ref in P that leads
to targetNode by overwriting that ref with C
(either left- or right-ref in P)
return
M = targetNode's inorder successor (i.e., left-most in-order
successor in targetNode's right subtree)
m = item in M
copy m into targetNode's item field
delete (treeNode, M)
return

Assuming the code for your BST is written correctly, explain to me how you are actually deleting the node? When I look at your code, your call to deleteItem(root, word) doesn't do anything at all. Regardless of what happens, it will return the root, which will then be applied to root.

You deleteItem() method never removes the node from the tree. That's why it fails.
Check this answer and the Wikipedia article on Binary Search Trees to better understand how deletions should be done.

Related

Java method not exiting properly with return statement

I am beginning to write a function for a binary tree. The method is currently aiming to find the node in the tree and return the node. The root value is data and the left and right are subtrees. When I step through it in the debugger when it gets to the return statement it jumps back into the second if block and will end up returning null.
#Override
public T successor(T d) {
T datas = null;
if (d.compareTo(this.data) < 0) //If its less than the root
left.successor(d);
if (d.compareTo(this.data) > 0) //If its less than the root
right.successor(d);
if (d.equals(this.data)){ //We found the node
datas = this.data;
}
return datas;
}
You are ignoring the returned value from the recursive call and end up return a null (datas) at a particular point in the call stack.
Change you code as:
T datas = null;
if (d.compareTo(this.data) < 0) //If its less than the root
datas = left.successor(d);
if (d.compareTo(this.data) > 0) //If its less than the root
datas = right.successor(d);
if (d.equals(this.data)){ //We found the node
datas = this.data;
}
return datas;
Or, you can remove the local variable and simplify it as
if (d.compareTo(this.data) < 0) {
return left.successor(d);
} else if (d.compareTo(this.data) > 0) {
return right.successor(d);
}
return this.data;
UPDATE: This assumes the value being searched for exists. You have to take care when left/right is null.

(Java) Alphabetical binary tree class doesn't work as expected

I'm required to develop my own binary search tree class for an assignment. It's supposed to sort a data class (User) which contains two strings, a username and a password, and then later be allowed to search it against a text file by the username variable. I tested the files for the assignment using a TreeSet successfully and found that the search will return about 300 positive matches if the whole search file is used. I thought based on my prior work that my binary tree class would work, but no matter what it never finds any matches via its search function, even though I know that's incorrect.
Some info on the code that isn't shown here-
The node (called BinTree) contains a single user class as well as a left and right node, as is normal.
getUserName is a simple accessor which directly retrieves userName from the node's underlying User.
the grow method is initially called on a manually created root node (b = new BinTree([first data from the file reader is entered here])
Since this is a program handling usernames, case sensitivity is important.
Based on the way the driver class is written, the search method does need to only return true if it finds a User with the correct userName or false otherwise.
Here's the (new) grow() method;
public BinTree grow(BinTree root, BinTree newNode)
{
if (root == null)
return newNode;
if (newNode.getUserName().compareTo(root.getUserName()) > 0)
{
if (root.right == null)
root.right = newNode;
else
root.right.grow(root.right, newNode);
}
else
{
if (root.left == null)
root.left = newNode;
else
root.left.grow(root.left, newNode);
}
return root;
}
And here's the (new) search() method;
public boolean search(BinTree r, String s) //search for a username, case sensitive
{
if (r.getUserName().compareTo(s) == 0)
return true;
else if (r.left != null && r.getUserName().compareTo(s) < 0)
return r.left.search(r.left, s);
else if (r.right != null)
return r.right.search(r.right, s);
return false;
}
Like I said, I had done a simpler binary tree before (that only held single int values, not custom data classes) and I've tried various ways of writing these two methods, but I feel like there's one crucial piece of the puzzle I'm just not getting. Thanks in advance for any help.
UPDATE: Thanks to #Diasiare and #c0der for pointing out the obvious issue that my code had typos regarding returns and left/right. I took that and changed the above code to reflect it. Upon running, the program still didn't seem to be working. I then wrote this show() method to print all the usernames stored in the Users of the tree.
public void show(BinTree r)
{
if (r != null)
{
show(r.left);
System.out.println(r.getUserName());
show(r.right);
}
} // show
When I called it after updating and compiling everything else, it did in fact show that the list was populated with usernames in alphabetical order. Here's a small snippet of the output (there's a lot of lines)
ted#wisler.com
teddy#borglum.com
teddy#winkey.com
-->teodoro#harkin.com<--
teodoro#stranford.com
teodoro#talaska.com
teodoro#willette.com
tera#norise.com
tera#strevels.com
terence#lathrum.com
terence#morocco.com
terence#neidig.com
terence#rabago.com
teresa#chauvin.com
teresa#dockus.com
The one singled out with arrows is one I manually searched through the search .txt file for and found. All told, with this new information I've determined that A) grow() is working correctly, as it populates the tree from the file in alphabetical order, and B) search() should be returning at least one match. Therefore, I'm working under the assumption that the problem lies in search() still.
UPDATE2: Here's the context in which I'm calling search() for those interested.
try
{
BufferedReader fromPasswords = new BufferedReader(new FileReader("passwordInput.txt"));
while ((line = fromPasswords.readLine()) != null)
{
System.out.println(line);
if(b.search(b, line))
{
matches++;
}
}
fromPasswords.close();
}
catch (Exception e)
{
System.out.println("Error while searching tree: " + e);
System.exit(0);
}
Your main problem is that the search function doesn't return the result of the recursive calls, it should read something like the following:
public boolean search(BinTree r, String s) //search for a username, case sensitive
{
if (r.getUserName().compareTo(s) == 0)
return true;
else if (r.left != null && r.getUserName().compareTo(s) < 0)
return r.left.search(r.left, s);
else if (r.right != null)
return r.right.search(r.right, s);
return false;
}
I would also like to note that this:
if (root == null)
root = newNode;
Doesn't make a lot of sense. If root is null you end up inserting newNode as a child of itself. Additionally your method has no way to comunicate that the tree was empty. I would recommend that you throw an NullPointerException, or make the method return the new tree. This will help if in the future you want it to be a balanced tree, in which case the root node might change. In that case the solution would look like this:
public BinTree grow(BinTree root, BinTree newNode)
{
if (root == null)
return newNode;
if (newNode.getUserName().compareTo(root.getUserName()) > 0)
{
if (root.right == null)
root.right = newNode;
else
root.right.grow(root.right, newNode);
}
else
{
if (root.left == null)
root.left = newNode;
else
root.left.grow(root.left, newNode);
}
return root;
}
Additionally as c0der notes the line root.left.grow(root.right, newNode); should probably be root.left.grow(root.left, newNode);

Find and return a Node in Binary Tree Java

Hi I'm trying to find a node equal to a string given by parametres and return that node. My structure is a binary tree of strings. We assume that the string searched exists.
The var q is inicializated to the root of the tree. (in the function I called the method find)
private NodeTree find(NodeTree q, String cont){
if(q._contingut.equals(cont)) return q;
else {
if(q._left!=null) return find(q._left,cont);
else if(q._right!=null)return find(q._right,cont);
}
return null;
}
In line 4 of the find() function you shouldn't return the result of recursive call over left subtree. Instead you should search the right subtree for the string if you get "NULL" from the left subtree.
Here is the updated code
private NodeTree find(NodeTree q, String cont){
if(q==NULL) return NULL;
if(q._contingut.equals(cont)) return q;
NodeTree result = NULL;
if(!q._left) result = find(q._left,cont);
if(!result && q._right) result = find(q._right,cont);
return result;
}
If your BST is built correctly, you need to decide where to look (left or right subtree) comparing current node value to query value, something like:
NodeTree find(NodeTree q, String query) {
if(q.value.equals(query))
return q;
else if (q.value.compareTo(query) > 0)
return q.left == null ? null : find(q.left, query);
else
return q.right == null ? null : find(q.right, query);
}

How to add an element to a linked list?

So a method for part of a project requires that I check to see that an E element is already in the node list. If not, then I add the element to the list and return true (as the method is type boolean). However, I keep getting an error in my JUnit test class. So I wanted to know what is wrong with my code currently. Here is the method:
public boolean add(E element)
{
for(Node ref = first; ref != null; ref = ref.next)
{
first = new Node(element);
if(!(element.equals(ref.data)))
{
n++;
add(element);
return true;
}
else if(element.equals(ref.data))
{
return false;
}
}
return false;
}
I'm pretty sure that the way I formatted the code is wrong. I'm not really familiar with nodes as I am with arrays so that is the reason why the code may be a disgrace. And btw, n is for size.
Your method appears to be combining both a recursive approach and an iterative approach to search, and in neither case do you handle the actual adding of the new element to the list.
You didn't specify where the new element should be added (front or back), so I'll assume front. I'm also assuming that first in your code is a field of your class, because it isn't otherwise declared.
A recursive solution doesn't make much sense here and doesn't have any advantages that I can see in this case. So here's one iterative solution, which puts the new element in front if not found:
public boolean add(E element)
{
for (Node ref = first; ref != null; ref = ref.next)
{
if (element.equals(ref.data)) {
return false;
}
}
Node newFirst = new Node(element);
newFirst.next = first;
first = newFirst;
return true;
}
The logic should have two parts:
First check in the complete list if the element exists or not? If it does return false and dont do anything else go to step 2.
If element does not exist then insert it and return true.
This is shown in the following code:
public boolean add(E element)
{
boolean doesElementExist = false;
//Step 1
for(Node ref = first; ref != null; ref = ref.next)
{
if(element.equals(ref.data))
{
doesElementExist = false;
break;
}
}
//Step 2
if(!doesElementExist) {
// Now we need to add element.
Node newNode = new Node(element);
newNode.next = first;
first = newNode;
doesElementExist = true;
}
return doesElementExist;
}

Equivalent subtree

I have two trees. The tree Node is defined as
class Node{
String treeId;
String type; //Each node has type which has fixed value. For example, its color: RED, BLANK, GREEN
Set<Node> children;
String ref; //The ref is a string and allowed value are "0", "1",..."10". The value is null if it is not leaf.
};
For leaf, the children set is empty.
I am wondering whether there is some existing efficient work done how to identify equivalent substree for two given tree. The equivalent is defined as:
1) Both subtree leaves are setsets leaves of original tree.
2) Both subtrees leaves have same ref value.
3) for non-leaves node, the equivalent refers to both node have same type and equivalent children.
Thanks. It would be better if there is some Java library addressing this problem.
The input should are two tree roots while output is the Node that is root of equivalent subtree. An the the tree's height is 100~ and it has more than 500 nodes.
What i did now is that I added a new field for class Node.
class Cache{
Map<String, Set<String>> map = new LinkedHashMap<String, Set<Str>>();
}
The key of map is Node id while the value is a ref set this node of this nodeid can reach. The Cache initiated when Node is initialized.
During isEquivalent compare phase, check whether overlap exists between two root's ref set. Return false if none.
I think this can help reduce the number of comparison space.
I am not sure about 1) Both subtree leaves are leaves of original tree. requirement as it seems to conflict with how to identify equivalent substree for two given tree.. Otherwise following recursive method should be able to cover other two conditions. The haveSameOriginalTree(r1, r2) method may be implemented to satisfy the first condition that I couldn't understand. r1 and r2 are roots of two subtrees that need to be checked for equivalence.
bool areEquivalent(Node r1, Node r2)
{
if(r1.children == null && r2.children == null)
{
return (haveSameOriginalTree(r1, r2) && (r1.ref == r2.ref));
}
if((r1.children == null && r2.children != null) || (r1.children != null && r2.children == null))
{
return false;
}
// if here then both must be non-leaf nodes
if(r1.type != r2.type)
{
return false;
}
if(r1.children.getCount() != r2.children.getCount()) // not sure of correct syntax for Java Sets
{
return false;
}
for(int i=0; i<r1.children.getCount(); i++)
{
if(!areEquivalent(r1.children[i], r2.children[i])) // again please correct the syntax for Sets
{
return false;
}
}
return true;
}
Let me know what you think.
Update
Here is an iterative version of the above solution. It uses stack data structure which is allocated on the heap rather than pushed on function's call stack, so not hugely different from recursive but still better. Also, since we only hold references to Nodes (rather than copying the whole object), this shouldn't be that much of an additional memory overhead if we are already loading the original tree into memory.
bool areEquivalent(Node r1, Node r2)
{
Stack<Node> s1 = new Stack<Node>();
Stack<Node> s2 = new Stack<Node>();
Node n1, n2;
s1.Push(r1);
s2.Push(r2);
while(true) // Need a better check
{
if(s1.getCount() != s2.getCount())
{
return false;
}
if(s1.getCount() == 0) // if both stacks are empty then we've traversed both trees without failure.
{
return true;
}
n1 = s1.Pop();
n2 = s2.Pop();
if(!areEquivalentNodes(n1, n2))
{
return false;
}
foreach(Node child in n1.children)
{
s1.Push(child);
}
foreach(Node child in n2.children)
{
s2.Push(child);
}
}
}
// only checks the two nodes are equivalent. their childrens' equivalence will be handled by other calls to this method.
bool areEquivalentNodes(Node n1, Node n2)
{
if(n1.children.getCount() != n2.children.getCount())
{
return false;
}
if(n1.children.getCount() == 0) // if both are leaf nodes...
{
if(n1.ref != n2.ref)
{
return false;
}
}
else // both are non-leaf
{
if(n1.type != n2.type)
{
return false;
}
// the condition that children of non-leaf nodes be equivalent will be covered by subsequent calls this method...
}
return true;
}
Please note that both solutions expect children of two equivalent nodes in the same order. If children are not ordered then we will need to sort them before calling above code.
Let me know if this is better.

Categories

Resources