How can I avoid a Stack Overflow using this Evaluation (BFS) - java

I have an NFA that I have constructed and I'm running this method to evaluate the machine to see if an expression is valid. This works for small regular expressions, but when the size of my regular expression and thusly the size of my NFA become too large, this search throws a stack overflow at me. I'm fairly certain it's because I've implemented a BFS, am using recursion, and am probably not handling my base cases very well.
This method takes an expression and a node (beginning with the start node of an NFA). First it checks if the length of the expression is zero, and if I'm in an accept node (a boolean value on the node), then I return true.
If the expression length is zero but the current node is not an accept node I return false.
If neither of these evaluate, then I get a list of all the nodes that the current node can reach using a "e" (epsilon) transition, and evaluate them.
If there are no "e" nodes, then I remove the first character from an input expression, make a shortened substring of the expression (removing the front of the expression), then look for a list of nodes that that node can reach using the removed character and the reduced expression.
If neither of these hit, then I return false
A basic Regular Expression is (a|b)*a
and an example of a evaluation expression would be aaaa
Which gets reduced at each pass, aaaa->aaa->aa->a->
private boolean evaluate(autoNode node, String expression)
{
if(expression.length()==0 && node.getAccept())
{
return true;
}
else if(expression.length()==0 && !node.getAccept())
{
return false;
}
String evalExp = expression.charAt(0)+""; //The first character in the expression
String redExp = expression.substring(1, expression.length());
//for each epsilon transition, evaluate it
if(node.getTransSet().contains("e"))
{
//if this node has an "e" transition then...
ArrayList<autoNode> EpsilonTransMap = node.getPathMap("e");
//The above ArrayList is a list of all the nodes that this node can reach
//using the "e" / epsilon transition
for(autoNode nodes : EpsilonTransMap)
{
if(evaluate(nodes, expression))
{
return true;
}
}
}
//for each transition on that key evaluate it
if(node.getTransSet().contains(evalExp))
{
//if this node has a transition from the front of the expression then...
ArrayList<autoNode> TransitionKeyMap = node.getPathMap(evalExp);
//The above ArrayList is a list of all the nodes that this node can reach
//on a transition equal to the "key" removed from the front of the expression String
for(autoNode nodes : TransitionKeyMap)
{
if(evaluate(nodes, redExp))
{
return true;
}
}
}
return false;
}
I'm aware that I've probably caused my own problem by using bfs searching instead of dfs. I'm wondering if someone can help me fix this and avoid a stack overflow by having too many things going on at once. Because while (a|b)*a can evaluate just fine...
((aa)+|(bb)+|(cc)+)(ba)(ca)
creates a rather large NFA, that causes a stack overflow on evaluating just:
"a"
Anything that doesn't result in me scrapping the method entirely would be great and appreciated.

Well, you don't actually have a DFS or a BFS in here, but that's not important. I guess it's also not important that you can't use regular expressions with the letter "e" in them.
What is important is that you're getting stack overflows whenever you reach a cycle of epsilon transitions. For example:
evaluate(n1,"aa") finds an epsilon transition from n1 to n2, and recurses:
evaluate(n2,"aa") which finds an epsilon transition from n2 to n1 and recurses:
evaluate(n1,"aa") .. and so on, recursing until the stack overflows.
There are a bunch of ways you could fix this... But even if you fix it this is still a pretty bad algorithm for evaluating NFAs -- it can take exponential time in the number of states!
EDIT -- so, here's the right way to do NFA evaluation, in pseudo code:
boolean evaluate(Node nfa, String str)
{
Set<Node> fromStates = new Set();
fromStates.add(nfa);
closeEpsilons(fromStates);
for (char chr in str)
{
if (fromStates.size()==0)
return false;
//find all the states we can get to from
//fromStates via chr
Set<Node> toStates = new Set();
for (Node fromState in fromStates)
{
//OP's code would say .getPathMap(chr) here
for(Node toState in fromState.getTransitionTargets(chr))
{
if (!toStates.contains(toState))
toStates.add(toState);
}
}
closeEpsilons(toStates);
//process the rest of the string with the state set we just found
fromStates = toStates;
}
//string is done. see if anything accepts
for(Node state in fromStates)
{
if (state.accepts())
{
return true;
}
}
return false;
}
//expand a state set with all states is reaches via epsilons
void closeEpsilons(Set<Node> states)
{
Queue<Node> processQueue = new Queue();
processQueue.addAll(states);
while(!processQueue.isEmpty())
{
Node fromState = processQueue.removeFirst();
//OP's code would say "getPathMap("e") here
for(Node toState in fromState.getEpsilonTargets())
{
if (!states.contains(toState))
{
//found a new state
states.add(toState);
//we'll have to search it for epsilons
processQueue.add(toState);
}
}
}
}

Related

Count the Characters in a String Recursively & treat "eu" as a Single Character

I am new to Java, and I'm trying to figure out how to count Characters in the given string and threat a combination of two characters "eu" as a single character, and still count all other characters as one character.
And I want to do that using recursion.
Consider the following example.
Input:
"geugeu"
Desired output:
4 // g + eu + g + eu = 4
Current output:
2
I've been trying a lot and still can't seem to figure out how to implement it correctly.
My code:
public static int recursionCount(String str) {
if (str.length() == 1) {
return 0;
}
else {
String ch = str.substring(0, 2);
if (ch.equals("eu") {
return 1 + recursionCount(str.substring(1));
}
else {
return recursionCount(str.substring(1));
}
}
}
OP wants to count all characters in a string but adjacent characters "ae", "oe", "ue", and "eu" should be considered a single character and counted only once.
Below code does that:
public static int recursionCount(String str) {
int n;
n = str.length();
if(n <= 1) {
return n; // return 1 if one character left or 0 if empty string.
}
else {
String ch = str.substring(0, 2);
if(ch.equals("ae") || ch.equals("oe") || ch.equals("ue") || ch.equals("eu")) {
// consider as one character and skip next character
return 1 + recursionCount(str.substring(2));
}
else {
// don't skip next character
return 1 + recursionCount(str.substring(1));
}
}
}
Recursion explained
In order to address a particular task using Recursion, you need a firm understanding of how recursion works.
And the first thing you need to keep in mind is that every recursive solution should (either explicitly or implicitly) contain two parts: Base case and Recursive case.
Let's have a look at them closely:
Base case - a part that represents a simple edge-case (or a set of edge-cases), i.e. a situation in which recursion should terminate. The outcome for these edge-cases is known in advance. For this task, base case is when the given string is empty, and since there's nothing to count the return value should be 0. That is sufficient for the algorithm to work, outcomes for other inputs should be derived from the recursive case.
Recursive case - is the part of the method where recursive calls are made and where the main logic resides. Every recursive call eventually hits the base case and stars building its return value.
In the recursive case, we need to check whether the given string starts from a particular string like "eu". And for that we don't need to generate a substring (keep in mind that object creation is costful). instead we can use method String.startsWith() which checks if the bytes of the provided prefix string match the bytes at the beginning of this string which is chipper (reminder: starting from Java 9 String is backed by an array of bytes, and each character is represented either with one or two bytes depending on the character encoding) and we also don't bother about the length of the string because if the string is shorter than the prefix startsWith() will return false.
Implementation
That said, here's how an implementation might look:
public static int recursionCount(String str) {
if(str.isEmpty()) {
return 0;
}
return str.startsWith("eu") ?
1 + recursionCount(str.substring(2)) : 1 + recursionCount(str.substring(1));
}
Note: that besides from being able to implement a solution, you also need to evaluate it's Time and Space complexity.
In this case because we are creating a new string with every call time complexity is quadratic O(n^2) (reminder: creation of the new string requires allocating the memory to coping bytes of the original string). And worse case space complexity also would be O(n^2).
There's a way of solving this problem recursively in a linear time O(n) without generating a new string at every call. For that we need to introduce the second argument - current index, and each recursive call should advance this index either by 1 or by 2 (I'm not going to implement this solution and living it for OP/reader as an exercise).
In addition
In addition, here's a concise and simple non-recursive solution using String.replace():
public static int count(String str) {
return str.replace("eu", "_").length();
}
If you would need handle multiple combination of character (which were listed in the first version of the question) you can make use of the regular expressions with String.replaceAll():
public static int count(String str) {
return str.replaceAll("ue|au|oe|eu", "_").length();
}

Find a complex element in a set of elements

I have a function that allows me to find a match between an incomplete element and at least one element in a set. An example of an incomplete element is 22.2.X.13, in which there is an item (defined with X) that could assume any value.
The goal of this function is to find at least one element in a set of elements that has 22 in the first position, 2 on the second, and 13 on the fourth.
For example, if we consider the set:
{
20.8.31.13,
32.3.29.13,
24.2.12.13,
19.2.37.13,
22.2.22.13,
27.17.22.13,
26.22.32.13,
22.3.22.13,
20.19.12.13,
17.4.37.13,
31.8.34.13
}
The output of the function return True since there are elements 22.2.22.13 which correspond to 22.2.X.13.
My function compares each pair of elements like strings and each item of the elements as an integer:
public boolean containsElement(String element) {
StringTokenizer strow = null, st = null;
boolean check = true;
String nextrow = "", next = "";
for(String row : setOfElements) {
strow = new StringTokenizer(row, ".");
st = new StringTokenizer(element, ".");
check = true;
while(st.hasMoreTokens()) {
next = st.nextToken();
if(!strow.hasMoreTokens()) {
break;
}
nextrow = strow.nextToken();
if(next.compareTo("X") != 0) {
int x = Integer.parseInt(next);
int y = Integer.parseInt(nextrow);
if(x != y) {
check = false;
break;
}
}
}
if(check) return true;
}
return false;
However, it is an expensive operation, particularly if the size of the string increases. Can you suggest to me another strategy or data structure to quickly perform this operation?
My solution is closely related to strings. However, we can consider other types for elements (e.g. array, list, tree node, etc)
Thanks to all for your answers. I have tried almost all the functions, and the bench:
myFunction: 0ms
hasMatch: 2ms
Stream API: 5ms
isIPMatch; 2ms
I think that the main problem of the regular expression is the time to create the pattern and match the strings.
You want to use Regex which is made exactly for tasks like this. Check out the demo.
22\.2\.\d+\.13
Java 8 and higher
You can use Stream API as of Java 8 to find at least one matching the Regex using Pattern and Matcher classes:
Set<String> set = ... // the set of Strings (can be any collection)
Pattern pattern = Pattern.compile("22\\.2\\.\\d+\\.13"); // compiled Pattern
boolean matches = set.stream() // Stream<String>
.map(pattern::matcher) // Stream<Matcher>
.anyMatch(Matcher::matches); // true if at least one matches
Java 7 and lower
The way is equal to Stream API: a short-circuit for-each loop with a break statement in case the match is found.
boolean matches = false;
Pattern pattern = Pattern.compile("22\\.2\\.\\d+\\.13");
for (String str: set) {
Matcher matcher = pattern.matcher(str);
if (matcher.matches()) {
matches = true;
break;
}
}
You can solve this by approaching the problem in a regex-based manner, as suggested by Nikolas Charalambidis (+1), or you can do it differently. To avoid being redundant with another answer, I will focus on an alternative approach here, using the split method.
public boolean isIPMatch(String pattern[], String input[]) {
if ((pattern == null) || (input == null) || (pattern.length <> input.length)) return false; //edge cases
for (int index = 0; index < pattern.length; index++) {
if ((!pattern[index].equals("X")) && (!pattern[index].equals(input[index]))) return false; //difference
}
return true; //everything matched
}
And you can call the method above in your loop, after converting the items to compare to String arrays via split.
For strings, regular expressions solve the task a lot better:
private boolean hasMatch(String[] haystack, String partial) {
String patternString = partial.replace("X", "[0-9]+").replace(".", "\\.");
// "22.2.X.13" becomes "22\\.2\\.[0-9]+\\.13"
Pattern p = Pattern.compile(patternString);
for (String s : haystack) {
if (p.matcher(s).matches()) return true;
}
return false;
}
For other types of objects, it depends on their structure.
If there is some kind of order, you could consider making your elements implement Comparable - and then you can place them into a TreeSet (or as keys in a TreeMap), which will always be kept sorted. This way, you can compare only against the elements that can match: mySortedSet.subSet(fromElement, toElement) returns only the elements between those two.
If there is no order, you will simply have to compare all elements against your "pattern".
Note that strings are comparable, but their default sorting order ignores the special semantics of your .-separators. So, with some care you can implement a treeset-based approach to make the search better-than-linear.
Other answers have already discussed using a regular expression by converting e.g. 22.2.X.13 to 22\.2\.\d+\.13 (don't forget to also escape the . or they mean "anything"). But while this will definitely be simpler and probably also a good bit faster, it does not lower the overall complexity. You still have to check each element in the set.
Instead, you might try to convert your set of IPs to a nested Map in this form:
{20: {8: {31: {13: null}}, 19: {12: {13: null}}}, 22: {2: {...}, 3: {...}}, ...}
(Of course, you should create this structure just once, and not for each search query.)
You can then write a recursive function match that works roughly as follows (pseudocode):
boolean match(ip: String, map: Map<String, Map<...>>) {
if (ip.empty) return true // done
first, rest = ip.splitfirst
if (first == "X") {
return map.values().any(submap -> match(rest, submap))
} else {
return first in map && match(rest, map[first])
}
}
This should reduce the complexity from O(n) to O(log n); more than that the more often you have to branch out, but at most O(n) for X.X.X.123 (X.X.X.X is trivial again). For small sets, a regular expression might still be faster, as it has less overhead, but for larger sets, this should be faster.

How to get around recursive stack overflow?

EDIT: Just to clarify, the recursion is required as part of an assignment, so it must be recursive even though I know that's not the best way to do this problem
I made a program that, in part, will search through an extremely large dictionary and compare a given list of words with each word in the dictionary and return a list of words that begin with the same two letters of the user-given word.
This works for small dictionaries but I just discovered that for dictionaries over a certain amount there is a stack limit for the recursions, so I get a stack overflow error.
My idea is to limit each recursion to 1000 recursions, then increment a counter for another 1000 and start again where the recursive method last left off and then end again at 2000, then so on until the end of the dictionary.
Is this the best way to do it? And if so, does anyone have any ideas how? I'm having a really hard time implementing this idea!
(edit: If it's not the best way, does anyone have any ideas of how to do it more effectively?)
Here is the code I have so far, the 1000 recursions idea is barely implemented here because I've deleted some of the code I tried in the past already but honestly it was about as helpful as what I have here.
the call:
for(int i = 0; i < givenWords.size(); i++){
int thousand = 1000;
Dictionary.prefix(givenWords.get(i), theDictionary, 0, thousand);
thousand = thousand + 1000;
}
and the prefix method:
public static void prefix (String origWord, List<String> theDictionary, int wordCounter, int thousand){
if(wordCounter < thousand){
// if the words don't match recurse through this same method in order to move on to the next word
if (wordCounter < theDictionary.size()){
if ( origWord.charAt(0) != theDictionary.get(wordCounter).charAt(0) || origWord.length() != theDictionary.get(wordCounter).length()){
prefix(origWord, theDictionary, wordCounter+1, thousand+1);
}
// if the words first letter and size match, send the word to prefixLetterChecker to check for the rest of the prefix.
else{
prefixLetterChecker(origWord, theDictionary.get(wordCounter), 1);
prefix(origWord, theDictionary, wordCounter+1, thousand+1);
}
}
}
else return;
}
edit for clarification:
The dictionary is a sorted large dictionary with only one word per line, lowercase
the "given word" is actually one out of a list, in the program, the user inputs a string between 2-10 characters, letters only no spaces etc. The program creates a list of all possible permutations of this string, then goes through an array of those permutations and for each permutation returns another list of words beginning with the first two letters of the given word.
If as the program is going through it, any letter up to the first two letters doesn't match, the program moves on to the next given word.
This is actually a nice assignment. Let's make some assumptions....
26 letters in the alphabet, all words are in those letters.
no single word is more than.... 1000 or so characters long.
Create a class, call it 'Node', looks something like:
private static class Node {
Node[] children = new Node[26];
boolean isWord = false;
}
Now, create a tree using this node class. The root of this tree is:
private final Node root = new Node ();
Then, first word in the dictionary is the word 'a'. We add it to the tree. Note that 'a' is letter 0.
So, we 'recurse' in to the tree:
private static final int indexOf(char c) {
return c - 'a';
}
private final Node getNodeForChars(Node node, char[] chars, int pos) {
if (pos == chars.length) {
return this;
}
Node n = children[indexOf(chars[pos])];
if (n == null) {
n = new Node();
children[indexOf(chars[pos])] = n;
}
return getNodeForChars(n, chars, pos + 1);
}
So, with that, you can simply do:
Node wordNode = getNodeForChars(root, word.toCharArray(), 0);
wordNode.isWord = true;
So, you can create a tree of words..... Now, if you need to find all words starting with a given sequence of letters (the prefix), you can do:
Node wordNode = getNodeForChars(root, prefix.toCharArray(), 0);
Now, this node, if isWord is true, and all of its children that are not-null and isWord is true, are words with the prefix. You just have to rebuild the sequence. You may find it advantageous to store the actual word as part of the Node, instead of the boolean isWord flag. Your call.
The recursion depth will never be more than the longest word. The density of the data is 'fanned out' a lot. There are other ways to set up the Node that may be more (or less) efficient in terms of performance, or space. The idea though, is that you set up your data in a wide tree, and your search is thus very fast, and all the child nodes at any point have the same prefix as the parent (or, rather, the parent is the prefix).

Morse Code - Binary Tree

How is the Morse Code representation of a letter determined?
"E" = "."
"T" = "-"
Why is it not alphabetic? As in let "A" = ".", "B" = "-", "C" =".-", etc.
I'm trying to develop an algorithm for a traversing Binary Tree filled with these letter.
My main aim is to search for a letter, like "A", but I don't know what conditions to use that determines when to branch to the right or left node.
EDIT
This is what I tried to do. Here I'm trying to keep a track of the path. But when I try it with a leter like "E", it says the root is empty.
static boolean treeContains( Node root, String item ) {
// Return true if item is one of the items in the binary
// sort tree to which node points. Return false if not.
if ( root == null ) {
// Tree is empty, so it certainly doesn't contain item.
System.out.print("Is null");
return false;
}
else if ( item.equals(root.element) ) {
// Yes, the item has been found in the root node.
return true;
}
else if ( item.compareTo(root.element) < 0 ) {
// If the item occurs, it must be in the left subtree.
// So, return the result of searching the left subtree.
res = res.concat(".");
return treeContains( root.right, item );
}
else {
// If the item occurs, it must be in the right subtree.
// So, return the result of searching the right subtree.
res = res.concat("-");
return treeContains( root.left, item );
}
} // end treeContains()
If you have a Binary Tree with letters in it, then let left be a dot (.) and right be a dash (-). As you traverse the tree, then you know what the binary code for each letter will is, by keeping track of the path.
EDIT
Looking at your code, you're not traversing the tree properly. First, I'm not sure what the variable res is, but I'm betting it's a static, which is not good coding practice.
Your real problem is that your comparison item.compareTo(root.element) < 0 is not a valid comparison for this tree. Instead, you should use a recursive call as your test, treeContains( root.right, item ). Only if this returns true can you then append the dot (.) to your res string. If it returns false, then you can make the recursive call using root.left and append a dash (-).
Personally, I would return a string from this method. The string would be the morse code for the letter so far, or null if the letter is not found. As you return back from the correct tree traversal, build up the correct string (what you're using for res right now).
Something to test for is that you may have to concat to the front of the string instead of the back of the string to get things to come out correct.
The real usefulness of this tree is in decoding the Morse code, converting a dot-dash string to its correct letter. This becomes a simple tree traversal.

Need some help in writing a recursive implementation for my Palindrome app

First of all I am not asking for people to "do my homework" like I have seen others on here ask for. I have managed to code a working iterative version of a program that determines if a string is a palindrome or not. Spaces, punctuation and special characters are ignored while determining if the string is a palindrome. This version does work but when I try and apply recursive statements in the "isPalindrome()" method I get Stack Overflow errors. I know what these errors are, it's just that applying a recursive method in a program like this is quite hard for me to get my head around (I only got taught about them 2 weeks ago). Anyway here is the code I have managed to compile (and run) so far:
/** Palindrome.java: A sigle application class that determines if a word or a string
* is a palindrome or not. This application is designed to ignore spaces between
* chars, punctuation marks and special characters while determining if the word or
* string is a palindrome or not.
*
**/
import java.util.Scanner;
import java.util.StringTokenizer;
import java.util.regex.*;
public class Palindrome{
static String palindrome, str, str2, str3;
/** The main method of the Palindrome application. Takes input from the
* user, removes spaces from their input, turns their string input into
* lowercase and then all non letter characters are taken out of the user's
* input. Finally the recursive method determines if the string entered in
* by the user is a palindrome.
*
* #param args Takes in a string array of arguements
**/
public static void main(String[] args){
Scanner input = new Scanner(System.in);
while(input.hasNext()){
str = removeSpaces(input.nextLine());
str2 = str.toLowerCase();
str3 = normalise(str2);
}
System.out.println(isPalindrome(str3));
}
/** The default constructor
**/
public Palindrome(){
}
/** isPalindrome(): A boolean method that is passed through a String input
* and uses a for loop, two inner while loops and an if-else to determine
* whether the users input is a palindrome.
*
* #param s The string input to be tested
* #return true The users input is a palindrome
* #return false The users input isn't a palindrome
**/
public static boolean isPalindrome(String s){
int first, last;
for(first = 0, last = s.length()-1 ; first < last ; first++ , last-- ){
while( (int)s.charAt(first) < 'a' || (int)s.charAt(first) > 'z' ){
first++;
}
while( (int)s.charAt(last ) < 'a' || (int)s.charAt(last ) > 'z' ){
last--;
}
}
if( first > last || s.charAt(first) != s.charAt(last) ){
//return isPalindrome(s.substring(0, s.length()-1)) == false;
return false;
}
else{
//return isPalindrome(s.substring(0, s.length()-1)) == true;
return true;
}
}
/**
* This method takes out punctuation marks in the string parsed
* through, using Java's regular expressions (regex) and Java's
* inbuilt method replaceAll(). The regex expression is passed
* through the replaceAll() method to remove all non alpha-numeric
* characters from the string passed through the method's parameter.
*
* #param t The string that will have punctuation stripped from it.
*
* #return t The string has had all non alpha-numeric characters
* removed and the new string is then returned.
*/
public static String normalise(String t){
t = t.replaceAll("[^a-zA-Z0-9]", "");
return t;
}
/** removeSpaces(): A method that deletes spaces from the users input
* and then decrements the string length count so any indexes aren't missed
* when it is incremented.
*
* #param s The string which is going to have it's spaces removed.
* #return temp The new string is then returned after the spaces have been taken out.
**/
public static String removeSpaces(String s){
StringBuilder temp = new StringBuilder(s); //creates a new StringBuilder with the inputted String
for(int i = 0; i < temp.length(); i++){ //do this for the entire length of the StringBuilder
if(temp.charAt(i) == ' '){ //if the char at i is a space
temp.deleteCharAt(i); //remove the char
i--; //subtract 1 from the counter so we don't miss an index when we increment it
}
}
return temp.toString(); //return the new String
}
}
I have blanked out the recursive statements in the recursive method for now. If someone can tell me what exactly I have done wrong and also help me in implementing a solution that would be really good. I would rather stick with the iterative version because I understand the mechanics of it, but have been asked to do a recursive version (I have been Java coding since after my mid year break last year but am a relative novice at recursion) which is proving to be quite a challenge. If you alter the code and it ends up working with the recursive version please explain how, when, why etc with your alterations. Am not looking for someone to just do this for me, I'm wanting to learn and it seems that I have learned best by example (I did get a B pass last year by analysing examples and reading explanations of implementations). Many thanks :).
EDIT: I think I have got the recursion going ok now, just the logic is the thing confusing me at the moment. Here is the recoded version of the isPalindrome() method:
public static boolean isPalindrome(String s){
int first, last;
boolean isPalindr = true;
if (s.length() <= 1){
return true; // Base case
}
for(first = 0, last = s.length()-1 ; first < last ; first++ , last-- ){
// while( (int)s.charAt(first) < 'a' || (int)s.charAt(first) > 'z' ){
// first++;
// }
// while( (int)s.charAt(last ) < 'a' || (int)s.charAt(last ) > 'z' ){
// last--;
// }
// }
if( first == last || s.charAt(first) == s.charAt(last) ){
//return isPalindrome(s.substring(first, last));
return isPalindrome(s.substring(first, last)) == true;
//isPalindr = false;
}
else{
return isPalindrome(s.substring(first, last)) == false;
//isPalindr = true;
}
}
return isPalindr;
}
If someone can help me with the logic I think this will be fixed :).
Removing all of the code that has nothing to do with the problem leaves us with this:
public static boolean isPalindrome(String s){
for loop {
isPalindrome();
}
}
isPalindrome calls isPalindrome calls isPalindrome, etc... infinitum.
The difference between this and a proper recursive function is that a recursive function will have some sort of conditional statement, breaking the cycle of the function calling itself. The flow of execution will go like this:
isPalindrome(1) begins execution and calls isPalidrome(2)
isPalindrome(2) begins execution and calls isPalidrome(3)
isPalindrome(3) begins execution and calls isPalidrome(4)
isPalindrome(4) begins execution and calls isPalidrome(5)
isPalindrome(5) begins execution and returns to isPalindrome(4)
isPalindrome(4) resumes execution and returns to isPalindrome(3)
isPalindrome(3) resumes execution and returns to isPalindrome(2)
isPalindrome(2) resumes execution and returns to isPalindrome(1)
isPalindrome(1) resumes execution and returns.
If that explanation doesn't help, think of it like this. Suppose someone was handing you plates, one at a time, to see if you can hold 25 plates at a time. It would go something like this:
Plate 1 is given to you. Are there 25 plates? No. Add another plate.
Plate 2 is stacked on top of Plate 1. Are there 25 plates? No. Add another plate.
Plate 3 is stacked on top of Plate 2. Are there 25 plates? No. Add another plate.
...
Plate 24 is stacked on top of Plate 23. Are there 25 plates? No. Add another plate.
Plate 25 is stacked on top of Plate 24. Are there 25 plates? Yes. Mission Accomplished. Now, let's put the plates back.
Plate 25 is removed.
Plate 24 is removed.
...
Plate 3 is removed.
Plate 2 is removed.
Plate 1 is removed.
Here's how that might be coded:
bool stackPlates(int i){
plateStack.addPlate();
if (plateStack.wasDropped == true) { return false; } // Were the plates dropped? Return FALSE to indicate failure.
else if (i < 25) { return stackPlates(i+1); } // Are there 25 plates yet? If not, add another.
else { return true; } // There are 25 plates stacked. Return TRUE to indicate success.
plateStack.removePlate(i);
}
Here's stackPlates(int i) called from another function:
bool success = stackPlates(1);
if (success==TRUE) { cout << "CONGRATULATIONS! YOU STACKED 25 PLATES!"; }
else { cout << "YOU BROKE THE PLATES! BETTER LUCK NEXT TIME!"; }
What your function needs to do in order to work properly is do this:
bool isPalindrome(string s, int i) {
char first = s[i]; // REPLACE THIS WITH THE CODE TO SKIP SPACES & SPECIAL CHARACTERS
char last = s[(s.length -1) -i]; // REPLACE THIS WITH THE CODE TO SKIP SPACES & SPECIAL CHARACTERS
if ( first != last ) { return false; } // return false if mismatch letter
else if ( i >= (s.length/2) ) { return true; } // return true if string fully checked
else { return isPalindrome(s, i+1); } // string not fully checked; move to next letter
}
You're experiencing stack overflows because the else branch at the bottom of the function is executed when (first <= last && "characters are equals"), so you keep recurring on the case where your string is composed by one character.
By the way, I think your code is not using recursion cleanly: you should preprocess your string only one time before starting recurring on the string, and the code that performs the palindrome recursion should be far simpler.
For any given entry into isPalindrome, it's going to recursively call itself regardless because you have no condition on your else. So, if it meets the criteria "first > last || s.charAt(first) != s.charAt(last)", it's going to recursively call isPalindrome, then the next call is too, even if it hits the else.
I don't know what a Palindrome is or what the real solution to the problem is, but that's why you're getting the stack overflow error. I suspect you need to add another condition to your else such that it will stop recursively calling itself.
When writing a recursive function the best way to go about this is usually to decide on a base case (:like "" is a palindrome, though so is "a" ... ) and then devise a method to take any state and move it to the base case.
So in the case of the palindrome, it's the same basic idea as before, if the first character and the last character are the same you return true and check the rest of the string ( thus moving closer to the base case ) and if they are not then you return false.
Your stack overflow comes from calling isPalindrome in every case rather than when you need to continue solving the problem, don't forget that if two characters mean that something isn't a palindrome, the rest is rendered irrelevant ( and thus needn't be recursed on )
Your recoded version is a bit strange, because it's still using a loop when it doesn't need to. In particular, your code will never go beyond the first iteration in your loop, because in the embedded if-else statement, you're going to return a result no matter what, so your function will always exit during the first iteration (unless there are no iterations at all).
Recursion should be approached by
Identifying a base case, i.e. a simplest case that can be solved
Re-representing a larger problem as a partial solution followed by the same, but smaller problem.
The base case you've handled correctly; any String which is length 1 or less is automatically a Palindrome.
The next step is to consider a larger problem, perhaps some string abcwewe....ba. How can we break this down into a simpler problem? We know that we'd normally check whether something is a palindrome by checking the letters one by one in pairs, starting at the ends, but then we also realise that each time we check the letters, we just repeat the same problem again and solve it the same way.
In the string I gave above, we check and verify that the first letter a is the same as the last letter a, so that's kind of a partial solution. Now we we end up with is the smaller word bcwewe....b, and it's the same problem again: Is this new String a palindrome also?
Thus, all you have to do now is to invoke the recursive call, but this time with the substring beginning with the 2nd character to the 2nd to last character. You can code the answer in just two lines, as below:
public static boolean isPalindrome(String s) {
if (s.length() <= 1) return true; // base case
return s.charAt(0) == s.charAt(s.length()-1) && isPalin(s.substring(1,s.length()-1)); // recursive case
}
One point to note is that I'm using the short circuit &&, so if the first condition fails (checking first and last character), then Java will not invoke the recursion.

Categories