I am new in Java and trying to add evaluate method to my class. The ExpTree class and its testing program is given to me. I wrote my code as I learned in the class, but do not know why it does not work.
An evaluate() method, which returns the arithmetic evaluation of the ExpTree. This should be done recursively, so you will need 2 methods to do it. In the case where it would result in division or mod by 0, it should throw a new ArithmeticException with a descriptive String. If the tree is empty, evaluate() should also throw a new ArithmeticException with a descriptive String.
Here is my code:
// This will implement an "Expression Tree" which stores an arithmetic expression
import java.util.*;
public class ExpTree
{
//-------data
private ExpNode root;
//-------constructor
public ExpTree()
{
root = null;
}
//constructor where a string is passed in. It is parsed and stored
public ExpTree(String expString)
{
//declare StringTokenizer, Stacks, and other variables used in parsing
StringTokenizer tokenizer = new StringTokenizer (expString, "()+-*/%", true);
String token;
ExpNode operator, leftOperand, rightOperand;
Stack<ExpNode> operators = new Stack<ExpNode>();
Stack<ExpNode> operands = new Stack<ExpNode>();
//break up expString into tokens
while (tokenizer.hasMoreTokens())
{
token = tokenizer.nextToken();
// if the current token is a left paren, ignore it
if (token.equals ("("))
;
// if the current token is an operator, put it on the
// operator stack
else if ((token.equals ("+")) || (token.equals ("-")) ||
(token.equals ("*")) || (token.equals ("/")) || (token.equals ("%")))
operators.push (new ExpNode(token));
//if the current token is a right paren, pop the operators stack
//to get the operator, pop the operands stack twice to get the two
//operands (stored as expression trees). Then make the two operands
//children of the operator and push back on the operands tree.
else if (token.equals (")"))
{
operator = operators.pop();
rightOperand = operands.pop();
leftOperand = operands.pop();
operator.setLeft(leftOperand);
operator.setRight(rightOperand);
operands.push(operator);
}
//otherwise, the token should be a number - put it in the operands stack
else
operands.push (new ExpNode(token));
} // while (tokenizer.hasMoreTokens())
//when finished parsing, the operands stack should contain the fully-built
//expression tree.
if (!operands.isEmpty())
root = operands.pop();
}
//-------methods
//isEmpty()
public boolean isEmpty()
{
return (root == null);
}
//printTree methods - prints the tree in RNL order, with indents. Called from "outside"
public void printTree()
{
if (root == null)
System.out.println("The tree is empty");
else
printTree(root, 0); //start with the root with 0 indentations
}
//recursive, private version of printTree
private void printTree(ExpNode subTree, int indents)
{
//if there is a right side, handle it first (with 1 more indent)
if (subTree.getRight() != null)
printTree(subTree.getRight(), indents+1);
//then print the node itself (first move over the right amount of indents)
System.out.println("\n\n\n");
for (int i=0; i<indents; i++)
System.out.print("\t");
System.out.println(subTree);
//if there is a left side, handle it first (with 1 more indent)
if (subTree.getLeft() != null)
printTree(subTree.getLeft(), indents+1);
}
//inorder traversal - starts the recursive calls to print inorder
public String inOrder()
{
return inOrder(root);
}
//inorder traversal - recursive left side of tree, print node, right side of tree
private String inOrder(ExpNode theTreeToTraverse)
{
if (theTreeToTraverse == null)
return ""; //don't try to do anything if tree is null
//else build up a String to return. It will involve recursive calls
String returnString = "";
if (theTreeToTraverse.getLeft() != null)
{
returnString += "(" + inOrder(theTreeToTraverse.getLeft());
}
returnString += theTreeToTraverse;
if (theTreeToTraverse.getRight() != null)
{
returnString += inOrder(theTreeToTraverse.getRight()) + ")";
}
return returnString;
}
//public version of evaluate
public double evaluate(){
if (root == null) //Am I null?
throw new ArithmeticException("The tree is empty, nothing to be evaluated!");
else //You handle it!
return recursiveEvaluate(root);
}
//Recursive version of evaluate
private double recursiveEvaluate(ExpNode subTree){
//If subTree is empty
if (subTree == null)
return 0;
//What are you subTree? A number? An operator?
else if(subTree.getData().equals("+"))
return recursiveEvaluate(subTree.getLeft()) +
recursiveEvaluate(subTree.getRight()) ;
else if(subTree.getData().equals("-"))
return recursiveEvaluate(subTree.getLeft()) -
recursiveEvaluate(subTree.getRight()) ;
else if(subTree.getData().equals("*"))
return recursiveEvaluate(subTree.getLeft()) *
recursiveEvaluate(subTree.getRight()) ;
else if(subTree.getData().equals("/")){
double right = recursiveEvaluate(subTree.getRight());
if(right == 0.0)
throw new ArithmeticException("Divide by zero is undefined!");
return recursiveEvaluate(subTree.getLeft()) / right;
}
else if(subTree.getData().equals("%")){
double right = recursiveEvaluate(subTree.getRight());
if(right == 0.0)
throw new ArithmeticException("Mod by zero exception");
return recursiveEvaluate(subTree.getLeft()) % right;
}
//Converting String type to double
else
return Double.parseDouble(subTree.getData());
}
//Public version of numPlus
public int numPlus(){
return recursiveNumPlus(root);
}
//Recursive version of numPlus
private int recursiveNumPlus(ExpNode subTree){
if (subTree == null)
return 0;
//If you are a '+' sign
if(subTree.getData().equals("+"))
return recursiveNumPlus(subTree.getLeft()) +
recursiveNumPlus(subTree.getRight()) + 1;
else
return recursiveNumPlus(subTree.getLeft()) +
recursiveNumPlus(subTree.getRight());
}
}
//***************************************************************************
// ExpNode holds a "node" for an ExpTree.
class ExpNode
{
//data
private String data;
private ExpNode left;
private ExpNode right;
//constructor
public ExpNode(String el)
{
data = el;
left = right = null;
}
//methods
//toString() - this is how an ExpNode represents itself as a String
public String toString()
{
return data;
}
//getLeft - returns the reference to the left subTree
public ExpNode getLeft()
{
return left;
}
//getRight - returns the reference to the right subTree
public ExpNode getRight()
{
return right;
}
//getData - returns the data (could be an operator or a number, so returns as a String)
public String getData()
{
return data;
}
//setLeft - sets the left subTree to whatever is passed in
public void setLeft(ExpNode newNode)
{
left = newNode;
}
//setRight - sets the right subTree to whatever is passed in
public void setRight(ExpNode newNode)
{
right = newNode;
}
}
The object oriented approach to your problem is to define a dedicated type for each kind of node. In order to keep the length of this answer reasonable and to avoid doing your homework, I'll only show a minimal example for integer expressions only involving addition and multiplication.
The first step is to define what an expression node must provide. For this, we define the interface ExprNode. If you did not learn about polymorphism in your class yet (which should surprise me) you'll probably want to stop reading now and come back after you have learned about it.
We want to evaluate nodes so we'll add an evaluate method that should return the value of the sub-expression rooted at that node. We'll defer its implementation to the specific node classes as these will know best how to evaluate themselves.
We also want to format expressions so we will add another method to format the sub-expression in infix notation.
public interface ExprNode {
int evaluate();
String asInfixString();
}
Now, let's see what nodes we need. Certainly, any expression will contain numbers at the leafs so we'll better start defining a class for them. The implementation of ValueNode is really simple, not to say trivial.
public final class ValueNode implements ExprNode {
private final int value;
public ValueNode(final int value) {
this.value = value;
}
#Override
public int evaluate() {
return this.value;
}
#Override
public String asInfixString() {
return String.valueOf(this.value);
}
}
Next, we have our two binary operations + and *. The implementation of the respective classes is again very simple.
public final class PlusNode implements ExprNode {
private final ExprNode lhs;
private final ExprNode rhs;
public PlusNode(final ExprNode lhs, final ExprNode rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
#Override
public int evaluate() {
return this.lhs.evaluate() + this.rhs.evaluate();
}
#Override
public String asInfixString() {
return String.format("(%s) + (%s)",
this.lhs.asInfixString(),
this.rhs.asInfixString());
}
}
public final class TimesNode implements ExprNode {
private final ExprNode lhs;
private final ExprNode rhs;
public TimesNode(final ExprNode lhs, final ExprNode rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
#Override
public int evaluate() {
return this.lhs.evaluate() * this.rhs.evaluate();
}
#Override
public String asInfixString() {
return String.format("(%s) * (%s)",
this.lhs.asInfixString(),
this.rhs.asInfixString());
}
}
Equipped with that, we can elegantly build expression trees, print and evaluate them. Here is an example for the expression 2 * (3 + 4).
ExprNode expr = new TimesNode(
new ValueNode(2),
new PlusNode(new ValueNode(3), new ValueNode(4)));
System.out.println(expr.asInfixString() + " = " + expr.evaluate());
It will print (2) * ((3) + (4)) = 14.
So, for your ExprTree, you would simply check if the root != null and if so, return root.evaluate().
What if we want more expressions?
Clearly, we will define another sub-type of ExprNode. For example, we can define more binary operators to handle subtraction and division, another unary node for the unary minus and so on. Each of these classes must implement the interface dictated by ExprNode so any sub-class can be used the same way, encapsulating the logic how it evaluates itself.
What if we want more operations?
For example, we might want to format expressions in postfix notation. To be able to do this, we could add another method asPostfixString to ExprNode. However, this is a little awkward since it means we'll have to go and edit all sub-classes we have implemented so far, adding the new operation.
This is a rather fundamental problem. If you lay out a composite structure to encapsulate operations in the nodes, then it is elegant to use and simple to add new node types but difficult to add mode operations. If you are using a case selection in every operation (somewhat like in your code) then it is simpler to add new operations but the code for the operations gets convoluted and it is difficult to add more node types (the code for all operations needs to be altered). This dilemma is known as the tyranny of the dominant model decomposition. The visitor pattern is an attempt to break out of it.
Anyway, if you are taking a basic Java class, I think what you are expected to learn is implementing a polymorphic tree with operations defined in the nodes as shown above.
Related
I need to build a heterogeneous(Elements with different types) BST and be able to sort the elements but I do not know how to approach the problem.
I've got the binary tree code right here.
This is the node class
public class Node<T> {
T data;
Node<T> left;
Node<T> right;
Node(T data) {
this.data = data;
left = null;
right = null;
}
}
And this is the tree class.
public class Tree<T extends Comparable<T>> {
private Node<T> root;
StringBuilder result = new StringBuilder();
public Tree() {
root = null;
}
public Node<T> getRoot() {
return root;
}
/**
* Method that inserts nodes into the binary tree. If the tree is empty , a new root node is
* initialized.
*
* #param root A node object.
* #param dataBeingInserted The object to be inserted on the tree.
* #return The root node object
*/
private Node<T> insertNode(Node<T> root, T dataBeingInserted) {
if (root == null) {
root = new Node<>(dataBeingInserted);
return root;
}
if (dataBeingInserted.compareTo(root.data) < 0) {
root.left = insertNode(root.left, dataBeingInserted);
} else if (dataBeingInserted.compareTo(root.data) > 0) {
root.right = insertNode(root.right, dataBeingInserted);
}
return root;
}
public void insertNode(T dataBeingInserted) {
root = insertNode(root, dataBeingInserted);
}
/**
* Method that recursively searches for our element through the tree. If the value is present in
* the root node , or there aren't any nodes in the tree , the method returns the root node. If
* the value we're looking for is smaller than the root node's value , we search for our value in
* the left subtree , otherwise we search for it in the right subtree.
*
* #param root A node object.
* #param dataBeingSearched User's value.
* #return Recursive call of the method.
*/
private Node<T> searchTree(Node<T> root, T dataBeingSearched) {
if (root == null || dataBeingSearched.compareTo(root.data) == 0) {
return root;
}
if ((dataBeingSearched.compareTo(root.data) > 0)) {
return searchTree(root.left, dataBeingSearched);
}
return searchTree(root.right, dataBeingSearched);
}
public Node searchTree(T dataBeingSearched) {
return searchTree(root, dataBeingSearched);
}
/**
* An implementation of the In-order traversal. First the left subtree is visited and printed
* accordingly, then we visit and print the root and after that we visit and print the right
* subtree.
*
* #param root The root node object.
*/
private String inorderTraversal(Node root) {
if (root == null) {
return "";
}
inorderTraversal(root.left);
result.append(root.data).append(" ");
inorderTraversal(root.right);
return result.toString();
}
public void inorderTraversal() {
inorderTraversal(root);
}
}
The problem with my tree right now is that I'm getting ClassCastException whenever any element is different than the root , because there what happens is the root defines the type of the tree and I can't fix that.
P.S
Here is the snippet that gives me the error (I will post the whole main method for convenience.)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Scanner;
public class Main {
private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
private static final Scanner SCANNER = new Scanner(System.in);
public static void main(String[] args) {
Tree tree = new Tree<>();
tree.insertNode(50);
tree.insertNode("30");
tree.insertNode('b');
tree.insertNode(69.3);
tree.inorderTraversal();
LOGGER.info("{}", tree.result);
}
}
For example there the first insert is an Integer , after which I try to insert a String and right there it's giving me the ClassCastException , saying that String is incomparable with Integer.
I think, the comments thoroughly elaborated that comparing any two objects is not sensibly possible. However, you can still implement such a tree by decoupling the comparison from the tree logic.
On the contrary, every client will be hit with the exact same problem you are facing right now, but some clients might have specific solutions that work for them. We'll look into that later.
First of all, Java already defines a Comparator interface that goes along with Comparable.
package java.util;
public interface Comparator<T> {
int compare(T o1, T o2);
}
At the same time, let's rethink the tree interface. Basically, the requirements state that it should be able to accept just about any object, so it must have a method like
public void add(Object data);
At this point, there is no reason to use generics, since we can't actually make any restrictions. Even if there are other objects in the tree, it should still be able to accept any object.
Therefore, we could do something like
public class Tree {
private Comparator<Object> comparator;
private Node root;
public Tree(Comparator<Object> comparator) {
this.comparator = Objects.requireNonNull(comparator);
}
public void add(Object data) {
root = insertNode(root, data);
}
private void insertData(Node root, Object dataBeingInserted) {
// see below
}
}
with no major changes to the Node class except that it's not generic anymore as well. Now, when comparing two objects in the insertNode method, we simply consult the Comparator instance instead of doing the comparison ourselves.
if (comparator.compare(dataBeingInserted, root.data) < 0) {
root.left = insertNode(root.left, dataBeingInserted);
} else if (comparator.compare(dataBeingInserted, root.data) > 0) {
root.right = insertNode(root.right, dataBeingInserted);
}
A client can use this Tree implementation with a Comparator that s/he limits to the types s/he knows may occur.
public static void main(String[] args) {
Tree t = new Tree((o1, o2) -> {
if (o1 instanceof Number && o2 instanceof String) {
// numbers before strings
return -1;
}
if (o1 instanceof Integer && o2 instanceof Integer) {
return ((Integer) o1).compareTo((Integer) o2);
}
if (o1 instanceof String && o2 instanceof String) {
return ((String) o1).compareTo((String) o2);
}
throw new ClassCastException("incompatible types: " + o1.getClass().getCanonicalName()
+ ", " + o2.getClass().getCanonicalName());
});
t.add("Hello");
t.add(Integer.valueOf(1337));
}
As indicated by the ClassCastException, this solution is still not able to handle any possible type inherently. However, this Tree implementation can be used to handle every heterogeneous combination of types (as long as a client defines an appropriate Comparator).
I had a similar task in my java course. I used generics to define the tree. Then I created a tree of an interface, which is implemented by every node. The methods of the interface are then implemented in every node and this allowed me to compare and sort the objects. There is a simple example in this topic: simple heterogeneous k-ary tree in java (for creating network simulator)
I thought of the problem as of the products in a grocery store. Every product has id, brand, name, price but depending of the type of product they have to be cooled or have "best before" or need to be frozen or are not edible.
This problem is not for an assignment, although it's a somewhat typical "assignment-like" problem that I'm trying to solve in a different way.
I want to write a method that will recursively go through a binary tree using a depth-first search algorithm to find a match for a character. Once it finds the matching character, I want it to return a string that maps the position of the character in the tree using 0s and 1s. "001",for example, would indicate that the character is found by going to the left node of the root node, the left node of that node, and then to the right node of that node.
Here is the code I have so far:
private static String encryptSearch(char c, BinaryNode curNode, String result)
{
char data = (char) curNode.getData();
if (data != c)
{
if (curNode.hasLeftChild())
{
result = result + "0";
encryptSearch(c, curNode.getLeftChild(), result);
}
if (curNode.hasRightChild())
{
result = result + "1";
encryptSearch(c, curNode.getRightChild(), result);
}
result = result.substring(0, result.length()-1);
}
return result;
}
The method is initially sent the character to be searched for, the root node, and null for the result. This method returns nothing except 0s. I think there are multiple problems with my code, but the largest one is that when the search reaches a leaf node, it returns. I've been unable to think of a way around this problem while still returning a string. I could easily write a void method that acts on the result string as an external variable, but I don't want to do that for the purpose of the exercise. Any help is appreciated!
Use the mutable StringBuilder instead of String. Also there should be a way to know that you got the result from left one (if any) before searching right one. So I suggest following changes.
private static boolean encryptSearch(char c, BinaryNode curNode, StringBuilder result) {
char data = curNode.getData();
if (data != c) {
boolean found = false;
if (curNode.hasLeftChild()) {
found = encryptSearch(c, curNode.getLeftChild(), result);
if (found) {
result.insert(0, "0");
return true;
}
}
if (curNode.hasRightChild()) {
found = encryptSearch(c, curNode.getRightChild(), result);
if (found) {
result.insert(0, "1");
return true;
}
}
return false; //no result
}
return true;
}
Firstly I ve searched about usage of Generic Types in java, however answers I ve found was way too simple or complicated. So here is my exact question.
I have three classes respectively PerfectTreeControl, Tree and Entry.
Tree has
public class Tree<K> {
public Entry <K> root;
Entry has
public class Entry<K> {
public K element;
public Entry<K> parent, left_child, right_child;
public Entry(K element) {
this.element = element;
}
public Entry(K element, Entry<K> left, Entry<K> right) {
left_child = left;
right_child = right;
this.element = element;
}
I am trying to understand what is the difference between Entry parent and Entry <K> parent? I know that K element can be used as integer, String or whatever I want, but does the same thing goes for the object? I tried to use Entry variables without parameter and it only said that Entry is a raw type and should be parameterized and it still working without error.
My second question is about checking out a tree whether its perfect or not. Here are the some codes I ve tried so far:
public class PerfectTreeControl {
public static boolean isPerfect(Tree<String> tree) {
Tree t1 = new Tree();
if( t1.isFull( tree.root ) ) {
int depth = t1.height(tree.root);
return t1.everyLeafHasSameDepth(tree.root, depth);
}
else
return false;
}
}
public class Tree<K> {
public Entry <K> root;
public boolean isLeaf(Entry e) {
return e.left_child == null &&
e.right_child == null;
}
public int height(Entry e) {
if( e == null ||
e.left_child == null &&
e.right_child == null )
return 0;
int left = height( e.left_child );
int right = height( e.right_child );
return 1 + Math.max(left, right);
}
public boolean isFull(Entry base) {
if( isLeaf(base) )
return true;
else
if( base.left_child != null && base.right_child != null ) {
return isFull(base.left_child) &&
isFull(base.right_child);
} else {
return false;
}
}
public int depth(Entry e) {
if( e == root ) {
return 0;
} else {
return 1 + depth(e.parent);
}
}
public boolean everyLeafHasSameDepth(Entry base, int depth) {
if( base == null )
return false;
else if(isLeaf(base) )
return depth( base ) == depth;
else {
return
everyLeafHasSameDepth(base.left_child, depth) &&
everyLeafHasSameDepth(base.right_child, depth);
}
}
entry class(I wrote it at the top of the page) As you can see, isPerfect method in the PerfectTreeControl class uses Tree -String- tree as a paramater and I have no idea what it is. In the Tree class, I tried Entry with and and again no difference. The code won't work properly, and I am totally confused.
Generics in Java are, fundamentally, a way to name a particular class within an object with knowing which class until that object is declared. This is useful because it allows the compiler to enforce consistency among references to that class.
More concretely, in your class Entry<K>, any time you reference K, the Java compiler will enforce that all references of type K are, in fact, treated as type K. For instance, if you create an object of type Entry<String>, the element member of that object must be of type String, the parent member must be of type Entry<String>, etc. If you had a method that returned a K, the compiler would recognize that the return value is String. If the compiler sees an inconsistency here - say, if you try to set member's value to an Integer - it will complain.
Keep in mind that qualities I describe in the example above is all in reference to the particular Entry<String> object that you've defined. If you instead define an Entry<Integer>, without updating your Entry class, the consistency is enforced within that new object - except this time with K meaning Integer.
If you create an object without specifying a type argument for K, you are using a "raw type". This prevents the compiler from enforcing consistency rules and it will assume that the type of K is Object. This means you'll have to start worrying about casting, which can be tedious to do properly.
To check if a tree is full (or "perfect"), the most intuitive approach is a recursive one. The recursive rule to use in this scenario is "if a tree's children are perfect and have the same depth, the tree is perfect."
I have a slight algorithmic problem. I think I miss something but can't exactly figure out what.
I want to walk to a tree containing strings and get out with a unique string.
Here is a graphical example of a tree I would like to parse.
My trees would have three different types of elements :
Boolean operators (OR, NOT, AND) => BE
other operators (like the =) => QO
leaves (last elements) =>LEAF
I would like to end up with something like this :
"LEAF QO LEAF BE LEAF QO LEAF "
For now, I use a recursive method: I check the current element of the tree, and re run the method on its children depending on the type of elements I have. For each step I would populate my final string.
public class SingleTest {
static String[] booleanElements = {"or", "and", "not"};
public static void main(String[] args) throws Exception {
CommonTree tree = (CommonTree)parser.parse().getTree();
if(true){
String where = "";
printWhere(tree, where);
System.out.println(where);
}
}
/*
* Print to where tests
*/
public static boolean isBooleanElement(CommonTree t){
return Arrays.asList(booleanElements).contains(t.toString().toLowerCase());
}
public static String printWhere(CommonTree t, String where){
//---------------------
// Checking node type
//---------------------
// Boolean Element
if (isBooleanElement(t)){
// Continue parsing the tree
for ( int i = 0; i < t.getChildCount(); i++ ) {
printWhere((CommonTree)t.getChild(i), where+ "BE");
}
}
// Last element of tree (LEAF)
else if(t.getChildCount() == 0 ){
where = where + "LEAF";
}
// query operator
else{
// Continue parsing the tree
for ( int i = 0; i < t.getChildCount(); i++ ) {
printWhere((CommonTree)t.getChild(i), where + "QO");
}
}
//---------------------
return where;
}
My problem is that this code :
String where = "";
System.out.println(printWhere(tree, where));
returns "" (Which is logical due to my implementation).
So my question is, how can I get to have a non void string as final output ?
Hope this is clear enough
Thank you for your help
Please note that this class is used for test purpose only, and I know that putting static everywhere is bad practice :)
EDIT :
The problem was (as expected) due to my lack of experience with recursion.
Here is my final code :
public static String printWhere(CommonTree t, String where){
//---------------------
// Checking node type
//---------------------
// Boolean Element
if (isBooleanElement(t)){
// Continue parsing the tree
for ( int i = 0; i < t.getChildCount(); i++ ) {
where = printWhere((CommonTree)t.getChild(i), where) + "BE";
}
}
// Last element of tree (LEAF)
else if(t.getChildCount() == 0 ){
where = where + "LEAF";
}
// query operator
else{
// Continue parsing the tree
for ( int i = 0; i < t.getChildCount(); i++ ) {
where = printWhere((CommonTree)t.getChild(i), where ) + "QO";
}
}
//---------------------
return where;
}
The problem is that you method printWhere does not return anything! You're appending the value to new where string, but since Java passes parameters by value, this newly created string is thrown away when you leave the method.
Make this method return string and return where at the end of it. Then, concatenate the result of a recursive call with the string from the above level. That's how recursion works.
Ok so say I have a function that looks for a specific word in a custom LinkedList class:
public LinkedList find(String word) {
if (this.word.equals(word))
return this;
if (next==null)
return null;
if (next.find(word)==next)
return next;
return null;
}
This code works fine, however it returns the FIRST found object that matches the criteria. What if I wanted to return the LAST object found that matches the paramater? I'm having a hard time figuring this out. Keep in mind I want to use recursion.
EDIT: What would be wrong with this code:
public LinkedList findLast(String word) {
LinkedList temp=new LinkedList(word, null);
if (next==null && next.word.equals(word))
return next;
if (next==null && !next.word.equals(word))
temp=next.findLast(word);
return temp;
}
Well, think of it this way: you need to recurse right to the end of the list, and then let the return value bubble up.
So the start of your method should either be a recursive call to look further down the list, or noting that we're at the end of the list - which is equivalent to the "further" result being null.
Now when you're returning, there are three options:
You've already found a match later than the current point - so return that reference
You've not found a match (so the return value of the recursive call was null) and:
The current point's word matches - so return the current point
The current point doesn't match - so return null
Hopefully that should be enough to get you to an implementation - if not, please ask more questions. I'd rather not give a full implementation when this is presumably homework.
Store a reference to the latest one found and keep on calling itself until it returns null -- then return the latest-reference.
Note, for clarification: you're going to have to iterate through your entire linked-list (unless you have a doubly-linked-list) to achieve this -- store a reference every time you find a match (but just overwrite the same reference each time) -- then return whatever the reference holds once you reach the end of this list.
public class LinkedList {
private static int uniqueIdCounter = 0;
private final String word;
private int uniqueId;
private LinkedList next = null;
public LinkedList( String word ) {
this.word = word;
this.uniqueId = uniqueIdCounter++;
}
#Override
public String toString() {
return this.word + "(" + this.uniqueId + ")";
}
public void setNext( LinkedList next ) {
this.next = next;
}
public LinkedList find( String word ) {
return this.find( word, null );
}
public LinkedList find( String word, LinkedList result ) {
if( this.word.equals( word ) ) {
result = this;
}
if( this.next != null ) {
result = this.next.find(word, result);
}
return result;
}
public static void main(String[] args) {
LinkedList head = new LinkedList( "A");
System.out.println( "Head is: " + head );
LinkedList B = new LinkedList( "B" );
head.setNext( B );
System.out.println( "B is: " + B );
LinkedList A2 = new LinkedList( "A" );
B.setNext( A2 );
System.out.println( "A2 is: " + A2 );
LinkedList last = head.find( "A" );
System.out.println( "Last is: " + last );
}
}
And here's the output:
Head is: A(0)
B is: B(1)
A2 is: A(2)
Last is: A(2)
Every straight recursive function has two places for some useful actions: before further method call and after:
function(n){
doBefore(n);
function(n+1)
doAfter(n)
}
doBefore() is executed "on the way forward", doAfter() is executed "on the way back". Now your algorithm checks word equality on the way forward. You have to modify your algorithm so that this check is performed on the way back.
public LinkedList find(String word, LinkedList result) {
if (this.word.equals(word))
result = this;
if (next != null )
return next.find(word, result)
return result;
Two-liner:
public LinkedList find(String word, LinkedList result) {
result = this.word.equals(word) ? this : result;
return next == null ? result : next.find(word, result);
#fprime: Ya, explanation: remember the result, replace it with later result, return when at the end.
Method with one argument:
public LinkedList find(String word){
result = this.word.equals(word) ? this : null;
if(next != null)
previous = next.find(word);
return (previous != null) ? previous : result
else
return result;
Just run it backwards from the tail.
public LinkedList find(String word) {
if (this.word.equals(word))
return this;
if (prev==null)
return null;
if (prev.find(word)==prev)
return prev;
return null;
}
To start with, you initial find(String word) does not work correctly.
Your first if statement is perfect. It is you success base case.
Your second if statement is also perfect. It is your failure base case.
Your third is where you go off the rails. You have handled all (in this case both) base cases, now all that is left is the recursive case. You don't need to check anything here. next.find(word) will return the correct answer, success or fail.
For findLast(String word), I can't add much to what Jon Skeet said. About the only advice I can add it to never have the a node check its neighbor. Each node should only ever check itself. You should see plenty of this.word.equals(word) but never next.word.equals(word).
public LinkedList find(String word) {
if(this.word.equals(word)) return this;
return next==null?null:next.find(word);
}
public LinkedList rfind(String word) {
if(next != null) {
LinkedList res = next.rfind(word);
if(res != null) return res;
}
return this.word.equals(word)?this:null;
}