简体   繁体   中英

Using Recursion to Reverse a String

I am trying to reverse a String word by word using recursion. (Ex: "Hello my friend" is reversed to "friend my Hello") This is the code I have attempted to write for this method. I have tried multiple similar variations but the output is only ever the first or last word of the String. I believe the part that is "broken" is the first if statement, but I am not quite sure.

public static String reverse (String words) {
   Scanner sc = new Scanner(words);
   String backwards = "";

   if (sc.hasNext()) {
     String currentWord = sc.next();
     reverse(sc.nextLine());
     backwards = backwards + " " + currentWord;
   } //end if
   else {
     backwards = words;
   } //end else

   return backwards;
 }

I am aware that a few similar questions exist, but their answers have not seemed to help me understand my mistake(s).

Thanks!

You shouldn't call nextLine() because your input is all on one line. Your logic is much clearer if you begin by creating a simple helper method, it should take an array of words and a position; from there you can recursively build your desired output with something like

private static String reverse(String[] words, int p) {
    if (p + 1 < words.length) {
        return reverse(words, p + 1) + " " + words[p];
    } else if (p < words.length) {
        return words[p];
    }
    return "";
}

Then your public method is easy to implement, just split the original input on white space and call reverse starting at 0 (remembering to return the result). Like,

public static String reverse(String words) {
    return reverse(words.split("\\s+"), 0);
}

And then, I tested it like

public static void main(String[] args) {
    System.out.println(reverse("Hello my friend"));
}

Which outputs (as requested)

friend my Hello

Alternatively , you could make that helper take your Scanner instead like

private static String reverse(Scanner sc) {
    if (sc.hasNext()) {
        String currentWord = sc.next();
        if (sc.hasNext()) {
            return reverse(sc) + " " + currentWord;
        }
        return currentWord;
    }
    return "";
}

And then your public method is

public static String reverse(String words) {
    return reverse(new Scanner(words));
}

Instead of using a Scanner , you can make use of an overload of String.split to split words around the first space:

public static String reverse(String words) {
    String[] wordArr = words.split(" ", 2); // split into a maximum of 2 Strings

    if (wordArr.length > 1) { // If there is more than 1 word
        // return the first word (wordArr[0]),
        // behind the reverse of the rest of the String (wordArr[1])
        return reverse(wordArr[1]) + " " + wordArr[0];
    }

    return wordArr[0]; // else, just return the one word
}

As stated in the comments, you could use a StringBuilder instead of Scanner class.

This example sends the same words, splits them by spaces each time you enter the method and you send the index of the word to be added in the next iteration.

For example:

public class RecursiveReverse {

    static StringBuilder sb = new StringBuilder();

    public static void main(String[] args) {
        String stringToReverse = "Hello my friend!";
        System.out.println(reverse(stringToReverse, stringToReverse.split(" ").length - 1));
    }

    public static String reverse(String words, int i) {
        if (i >= 0) { //If the index of the words is greater or equals the first word
            sb.append(words.split(" ")[i]); //We split it and append it to our StringBuilder
            sb.append(" "); //We append a space
            reverse(words, --i); //We do this again
        }
        return sb.toString(); //When the above condition doesn't match we return the StringBuilder object as a String (which contains the words reversed)
    }
}

Which produces this output:

friend! my Hello 

A better method would be passing a String array as parameter so you split only once (when sending the words as an array to the method) the String.

public class RecursiveReverse {

    static StringBuilder sb = new StringBuilder();

    public static void main(String[] args) {
        String stringToReverse = "Hello my friend!";
        String words[] = stringToReverse.split(" ");
        System.out.println(reverse(words, words.length - 1));
    }

    public static String reverse(String words[], int i) {
        if (i >= 0) {
            sb.append(words[i]);
            sb.append(" ");
            reverse(words, --i);
        }
        return sb.toString();
    }
}

You throw away the recursion results:

 reverse(sc.nextLine());
 backwards = backwards + " " + currentWord;

Instead, use this:

 backwards = reverse(sc.nextLine());
 backwards = backwards + " " + currentWord;

Better yet:

 backwards = reverse(sc.nextLine()) + " " + currentWord;
public static String reverseSentence(String sentence) {

    StringBuilder sb = new StringBuilder();

    int firstSpace = sentence.indexOf(' ');
    if (firstSpace == -1) {
        return sb.append(sentence.strip()).append(" ").toString();
    }
    String secondPart = sentence.substring(firstSpace + 1); 
    String firstPart = sentence.substring(0, firstSpace);//similar to merger sort 
    return sb.append(reverseSentence(secondPart)).append(reverseSentence(firstPart)).toString();
}

Do you must use recursion? You can do that without it.

public static String reverse(String words) {
    String[] list = words.split(" ");
    Collections.reverse(list);
    String reversed = String.join(" ", list);
    return reversed;
}

You must keep hold of the extracted words between calls in an accumulator. Here is an example.

public static String reverse(String words, String acc){
    Scanner sc = new Scanner(words);

    if(!sc.hasNext()){
        return acc;
    }

    return reverse(sc.nextLine(), acc) + " " + sc.next();
}

You would call it like this.

reverse("Hello my friend", "");

It's not the most efficient implementation in the world, but yeah... It must work!

If you want a more efficient one, use a StringBuilder as the accumulator.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM